> For a complete page index, fetch https://docs.synthflow.ai/llms.txt. For full documentation content, fetch https://docs.synthflow.ai/llms-full.txt.

# Batch calling

> Create batches of outbound calls with the Platform API and control scheduling, calling windows, dialing speed, and recipient outcomes.

This feature is available only on our Enterprise plan. Please reach out your account rep to gain access

Batch calling lets you send one API request with a list of recipients and have Synthflow dial them over time, as fast as your account's concurrency allows. Batches appear as campaigns in the Synthflow portal, and every dialed recipient becomes a regular call with the same billing, call records, and [webhooks](/webhooks) as a single outbound call.

A batch is created with one request, then managed through its `batch_call_id`: you can [check its status](/api-reference/platform-api/batch-calls/get-a-batch-call), [list its recipients](/api-reference/platform-api/batch-calls/list-batch-call-recipients), [pause](/api-reference/platform-api/batch-calls/pause-a-batch-call), [resume](/api-reference/platform-api/batch-calls/resume-a-batch-call), or [cancel](/api-reference/platform-api/batch-calls/cancel-a-batch-call) it at any time.

## Create a batch call

Use [`POST /v2/calls/batch`](/api-reference/platform-api/batch-calls/create-a-batch-call). A new batch needs the `model_id` of the agent that places the calls, the `from_phone_number` used as caller ID, and between 1 and 10,000 `tasks` (recipients).

```bash
curl --request POST \
  --url https://api.synthflow.ai/v2/calls/batch \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --header 'Content-Type: application/json' \
  --data '{
    "name": "August re-engagement",
    "model_id": "1631a495-e29b-4933-a0b7-3ea0f4a45e64",
    "from_phone_number": "+16286666348",
    "tasks": [
      {
        "id": "lead-0001",
        "to_phone_number": "+12025551234",
        "lead_name": "John Doe",
        "custom_variables": { "plan": "pro" }
      }
    ]
  }'
```

Successful response:

```json
{
  "status": "ok",
  "response": {
    "batch_call_id": "3f6f2f5e-4f4b-4f0e-9a4e-2b6d1a5c7e90",
    "name": "August re-engagement",
    "from_phone_number": "+16286666348",
    "scheduled_timestamp": null,
    "total_task_count": 1,
    "call_time_window": null,
    "status": "scheduled"
  }
}
```

Each recipient accepts an optional `id` of your own choosing, unique within the batch. If you retry a request or append a recipient whose `id` already exists in the batch, that recipient is skipped instead of being called twice. Recipients sent without an `id` are never deduplicated. A recipient can also set `override_model_id` to be called by a different agent than the rest of the batch, and `custom_variables` to inject prompt variables into its call.

The agent and phone number are not validated when the batch is created. If the `from_phone_number` is not attached to your workspace, every recipient fails at dialing time with the reason in its `error_message`.

## Schedule dialing and calling windows

Two optional fields control when recipients are dialed:

* `trigger_timestamp` is a Unix timestamp in milliseconds. Dialing starts at that time; omit it to start immediately.
* `call_time_window` restricts dialing to weekly time windows, evaluated in the agent's timezone. When set, it overrides the agent's working hours for this batch. When it is omitted, the agent's own working hours apply, and if neither is configured, recipients are dialed at any time.

```json
{
  "call_time_window": {
    "weekly_hours": {
      "monday": [{ "start": "09:00", "end": "17:00" }],
      "tuesday": [{ "start": "09:00", "end": "12:00" }, { "start": "13:00", "end": "17:00" }]
    }
  }
}
```

Days use lowercase names (`monday` to `sunday`), times use `HH:MM` in 24-hour format, `start` must be earlier than `end` (windows cannot span midnight), and a day can hold several windows as long as they do not overlap. A missing or empty day means no calls on that day. When the window closes, dialing stops and picks up again the next time it opens; nothing is lost.

## Add more recipients to a batch

A single request accepts at most 10,000 recipients. To go beyond that, or to feed a long-running batch, send [`POST /v2/calls/batch`](/api-reference/platform-api/batch-calls/create-a-batch-call) again with the `batch_call_id` of the existing batch:

```bash
curl --request POST \
  --url https://api.synthflow.ai/v2/calls/batch \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --header 'Content-Type: application/json' \
  --data '{
    "batch_call_id": "3f6f2f5e-4f4b-4f0e-9a4e-2b6d1a5c7e90",
    "tasks": [
      { "id": "lead-0002", "to_phone_number": "+12025556789" }
    ]
  }'
```

In append mode `model_id`, `from_phone_number`, `trigger_timestamp`, `reserved_concurrency`, and `call_time_window` are ignored; the batch keeps its original configuration. Appending to a `completed` or `canceled` batch returns a 400 error. The response's `total_task_count` is the batch's new total after deduplication.

## Control dialing speed

Dialing speed is bounded by your account's maximum concurrent calls. Every dialing cycle, the dispatcher counts your workspace's currently active calls (batch and non-batch alike) and only places as many new calls as fit under the limit, up to roughly 20 new calls per second.

Set `reserved_concurrency` to hold back capacity for calls outside the batch, such as inbound traffic or one-off outbound calls. With a limit of 10 concurrent calls and `reserved_concurrency: 4`, the batch never uses more than 6 slots. The value must be lower than your account's maximum concurrent calls, otherwise the request fails with a 400 error.

## Monitor a batch

[`GET /v2/calls/batch/{batch_call_id}`](/api-reference/platform-api/batch-calls/get-a-batch-call) returns the batch's status and counters, and [`GET /v2/calls/batch`](/api-reference/platform-api/batch-calls/list-batch-calls) lists all batches in the workspace, most recent first. A batch has one of five statuses:

| Status        | Meaning                                                                                             |
| ------------- | --------------------------------------------------------------------------------------------------- |
| `scheduled`   | The batch is accepted and dials recipients when its start time, calling window, and capacity allow. |
| `in_progress` | Recipients are being dialed.                                                                        |
| `paused`      | Dialing is suspended; queued recipients are kept and dialing resumes on request.                    |
| `completed`   | Every recipient was dialed or failed; nothing is left to do.                                        |
| `canceled`    | The batch was canceled; queued recipients will never be dialed.                                     |

### Track individual recipients

[`GET /v2/calls/batch/{batch_call_id}/tasks`](/api-reference/platform-api/batch-calls/list-batch-call-recipients) lists every recipient with its outcome, and accepts a `status` filter:

| Status       | Meaning                                                     |
| ------------ | ----------------------------------------------------------- |
| `pending`    | Waiting to be dialed.                                       |
| `claimed`    | Picked up by the dispatcher, about to be dialed.            |
| `dispatched` | The call was placed; `call_id` links to the call record.    |
| `failed`     | The call could not be placed; `error_message` explains why. |
| `canceled`   | The batch was canceled before this recipient was dialed.    |

Common `error_message` values include `Insufficient minutes remaining. Please top up or switch to a higher plan.`, `Please attach a phone number to the agent`, and `Call cannot be initiated outside the configured working hours`.

To work with the calls themselves (transcripts, recordings, outcomes), filter the [list calls endpoint](/api-reference/platform-api/calls/list-calls) with `?batch_call_id=`. That list only contains recipients whose call was actually placed, while the recipients endpoint also covers queued recipients and failures that never produced a call.

## Pause, resume, and cancel

[`POST /v2/calls/batch/{batch_call_id}/pause`](/api-reference/platform-api/batch-calls/pause-a-batch-call) suspends dialing. Queued recipients stay queued, and calls already in progress continue normally. [`POST /v2/calls/batch/{batch_call_id}/resume`](/api-reference/platform-api/batch-calls/resume-a-batch-call) restarts dialing within a few seconds, continuing with the recipients that were still queued.

[`POST /v2/calls/batch/{batch_call_id}/cancel`](/api-reference/platform-api/batch-calls/cancel-a-batch-call) permanently stops the batch: queued recipients are marked `canceled` and will never be dialed, while calls already in progress are not hung up. A canceled batch cannot be resumed and cannot take new recipients. All three endpoints return the batch's updated status.

## FAQ

#### How many recipients can a batch hold?

A single request accepts up to 10,000 recipients. The batch itself has no size limit; append more recipients with additional requests carrying the `batch_call_id`.

#### Are batch calls billed differently from regular calls?

No. Every dispatched recipient becomes a regular outbound call with the same billing, call records, and webhook events as a call created through the calls endpoint.

#### Do I get a webhook when the batch finishes?

No. Webhooks fire per call, not per batch. Poll the batch status endpoint to detect completion: the batch turns `completed` when no recipients are left to dial.

#### What happens if a recipient's call fails?

The recipient is marked `failed` with the reason in `error_message`, and the batch moves on. Failed recipients are not retried automatically; re-send them in an append request (with a new `id`, or no `id`) to try again.

#### Does canceling a batch hang up calls that are already connected?

No. Cancel only removes recipients that have not been dialed yet. Connected calls run to their natural end.

#### Can I change the calling window after the batch is created?

The batch's own `call_time_window` cannot be changed after creation. If the batch relies on the agent's working hours instead, editing those hours takes effect on the next dialing cycle.