Skip to main content
The API Quickstart makes one call. This guide scales that to a campaign: upload a CSV of recipients (each with their own variables), schedule the batch, and pull back per-call results. It reuses the agent and execution concepts from the outbound quickstart, so start there if you haven’t.
You’ll need an agent_id (see the API Quickstart) and enough wallet balance for the number of rows in your CSV.

Prerequisites

  • export BOLNA_API_KEY="bn-xxxx" and export BOLNA_AGENT_ID="your-agent-id"
  • A CSV with a contact_number column (required). Any other columns become {variable} values your prompt and welcome message can use.
Reference CSV columns in the Agent Tab with {column_name} — e.g. a customer_name column lets your welcome message say “Hi ”.

The CSV

batch.csv
contact_number,customer_name,appointment_day
+919876543210,Asha,Friday
+919812345678,Ravi,Monday
Only contact_number is mandatory; customer_name and appointment_day are example variables.
1

Create the batch

Upload the CSV with your agent_id as multipart/form-data.
curl https://api.bolna.ai/batches \
  -H "Authorization: Bearer $BOLNA_API_KEY" \
  -F "agent_id=$BOLNA_AGENT_ID" \
  -F "file=@batch.csv"
Response
// HTTP 201 Created
{ "batch_id": "abcdefghijklmnopqrstuvwxyz012345", "state": "created" }
Save the batch_id.
2

Schedule the batch

A created batch doesn’t call anyone until it’s scheduled. Pass scheduled_at as an ISO 8601 timestamp with a numeric UTC offset (e.g. +00:00). Two API rules that aren’t obvious:
  • The time must be at least 2 minutes in the future, or you get a 400.
  • Use a numeric offset like +00:00 — the Z suffix is rejected with a 500.
  • Bolna rounds the start up to the next 10-minute mark, so a 12:02 request runs at 12:10.
curl https://api.bolna.ai/batches/BATCH_ID/schedule \
  -H "Authorization: Bearer $BOLNA_API_KEY" \
  -F "scheduled_at=2026-06-23T18:30:00+00:00"
Response
{ "message": "success", "state": "scheduled at 2026-06-23T07:00:00+00:00" }
Outbound time-of-day restrictions still apply per recipient timezone. If a scheduled time falls outside the allowed window, calls are pushed to the next allowed slot. See Calling Guardrails.
3

Track progress

Poll the batch for its status, then list per-call executions once it’s running or done.
curl https://api.bolna.ai/batches/BATCH_ID \
  -H "Authorization: Bearer $BOLNA_API_KEY"
Each execution in the list is a standard execution objectstatus, transcript, total_cost, telephony_data, extracted_data, etc. — one per row in your CSV. The batch object tracks overall progress; status moves created → scheduled → running → completed.
GET /batches/{batch_id}
{
  "batch_id": "c4e37bb6…",
  "status": "running",
  "scheduled_at": "2026-06-23T07:00:00+00:00",
  "valid_contacts": 1,
  "total_contacts": 1,
  "file_name": "batch.csv",
  "execution_status": { "completed": 1 }
}
GET /batches/{batch_id}/executions (bare array)
[
  {
    "id": "1ae3e573-…",
    "status": "completed",
    "user_number": "+919876543210",
    "conversation_duration": 18.0,
    "total_cost": 3.26,
    "transcript": "assistant: Hi! …",
    "context_details": {
      "recipient_data": { "customer_name": "Asha", "appointment_day": "Friday" }
    },
    "telephony_data": { "recording_url": "https://api.bolna.ai/recordings/call/1ae3e573-…" }
  }
]
The execution’s context_details.recipient_data echoes back the CSV columns for that row — confirming your {variables} reached the call. Per-call status starts at prepared before the call is placed, then becomes completed (or no-answer, busy, failed).

Run the whole flow as one script

The dependency-free bolna_batch_test.py script does all of the above — builds a sample CSV (or takes your own), creates the batch, schedules it, polls, and prints per-call results:
export BOLNA_API_KEY="bn-xxxx"
export BOLNA_AGENT_ID="your-agent-id"

python3 bolna_batch_test.py --dry-run --to "+919876543210" --name "Asha"   # inspect first
python3 bolna_batch_test.py --to "+919876543210" --name "Asha"             # real campaign
python3 bolna_batch_test.py --csv my-recipients.csv                        # your own list

Managing batches

ActionEndpoint
Stop a running batchPOST /batches/{batch_id}/stop
Get one batchGET /batches/{batch_id}
List a batch’s callsGET /batches/{batch_id}/executions
List all batches for an agentGET /batches/{agent_id}/all
Delete a batchDELETE /batches/{batch_id}

Next steps

Personalize each call

Use CSV columns as {variables} in your prompt and welcome message.

Receive results via webhook

Get each call’s payload pushed to you instead of polling.

Extract structured data

Auto-capture lead quality, outcomes, and more from every call.

Single calls

The outbound quickstart this builds on.