> ## Documentation Index
> Fetch the complete documentation index at: https://docs.getdialed.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Batched delivery

> Converge records from many concurrent executions into bulk calls to a downstream system

When a job fans a file of ten thousand contacts out into ten thousand executions, and each execution ends with "add this contact to a dialing list," you don't want ten thousand individual API calls hitting the downstream platform. Batched delivery solves this: each execution contributes its record to a shared buffer, and the platform delivers the buffer to the downstream system in bulk calls.

## When to use it

Use batched delivery when a task performs a **high-volume write against a rate-limited or bulk-oriented API** — Five9 list operations are the canonical example. Many contact center APIs accept hundreds or thousands of records per call and throttle callers who send them one at a time.

Keep the default direct delivery when a task is low-volume, needs its own response immediately, or calls an API with no bulk endpoint.

<Note>
  Only actions marked `batch_capable` in the [catalog](/concepts/catalog) support batched delivery. A batch-capable action also declares `batch_settings`, including `max_records_per_call` for the downstream API.
</Note>

## Enabling it on a task

Set `delivery: "batched"` on the task and provide a `batch_config`. A batched task without a `batch_config` is rejected at creation time.

```json theme={null}
{
  "task_id": "add_contact",
  "task_name": "Add to dialing list",
  "platform_id": "five9",
  "service_id": "five9__configuration_service",
  "action_id": "five9__configuration_service__add_to_list",
  "connection_id": "conn_e5f6g7h8",
  "delivery": "batched",
  "batch_config": {
    "batch_group_key": "list_name",
    "max_batch_size": 500,
    "flush_interval": "30s"
  },
  "parameters": {
    "list_name": "{{var.target_list}}",
    "number1": "{{input.phone}}",
    "first_name": "{{input.first_name}}"
  }
}
```

## How it behaves

When a batched task runs, the execution resolves the task's [expressions](/concepts/expressions) as usual, then hands the resulting record to a shared batch collector for that action instead of calling the downstream API itself. Records from **every concurrent execution** using the same action converge into the same buffer. The execution pauses at that task until its record's result comes back, then continues with the rest of the flow — from the flow's point of view, a batched task looks just like a direct one, only with added latency while the batch fills.

The buffer flushes — one bulk call to the downstream system — when either trigger fires, whichever comes first:

* **Size** — the buffer reaches `max_batch_size` records.
* **Time** — `flush_interval` elapses since the last flush.

If fewer than `min_batch_size` records have accumulated when the interval elapses, the flush is deferred to let the batch fill further. Records are never held indefinitely, though: after a few consecutive deferrals, a **force-flush** delivers whatever is buffered, even below the minimum.

<Tip>
  Tune `max_batch_size` to the downstream API's per-call record limit and `flush_interval` to the latency you can tolerate. A large `min_batch_size` improves bulk efficiency at the cost of latency during quiet periods.
</Tip>

## Configuration reference

| Field               | Type    | Default                 | Description                                                                                                                                                   |
| ------------------- | ------- | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `batch_group_key`   | string  | (required)              | Record field used to group records that must be delivered together — for example, `list_name` so each bulk call targets a single list.                        |
| `flush_strategy`    | string  | `threshold_or_interval` | What triggers a flush: `threshold_or_interval`, `threshold_only`, or `interval_only`.                                                                         |
| `max_batch_size`    | integer | `1000`                  | Maximum records per bulk call. Reaching it triggers an immediate flush.                                                                                       |
| `flush_interval`    | string  | `"60s"`                 | Maximum time between flushes, as a duration string (e.g. `"30s"`, `"5m"`).                                                                                    |
| `min_batch_size`    | integer | `1`                     | Minimum records before an interval-based flush proceeds; smaller batches wait, up to the force-flush limit.                                                   |
| `priority`          | string  | `normal`                | Batch priority: `normal` or `high`.                                                                                                                           |
| `on_batch_failure`  | string  | `retry_batch`           | What happens when a bulk call fails: `retry_batch` retries the whole batch, `abort_batch` gives up on it.                                                     |
| `on_record_failure` | string  | `signal_failed`         | How individual failed records are handled: `signal_failed` reports the failure back to the originating execution, `signal_and_retry` also retries the record. |

## Failure handling

Failures can happen at two levels:

* **The bulk call fails** (network error, downstream outage, rejected batch). `on_batch_failure` governs the response — with the default `retry_batch`, transient failures are retried automatically with backoff before the batch is declared failed.
* **Individual records fail** inside an otherwise-successful bulk call, on actions whose downstream API supports partial success. `on_record_failure` governs what each originating execution sees: with the default `signal_failed`, the execution receives the failure as its task result and can branch on it with an exit condition.

Each execution receives the result for **its own record**. One record failing does not fail the other executions whose records shared the same bulk call.

<Warning>
  An execution waits up to 10 minutes for its record's batch result. If no result arrives in that window — for example, the downstream system is down and retries are exhausted — the task's result is a `batch_timeout` error, which the flow can detect with an exit condition.
</Warning>

## Next steps

<CardGroup cols={2}>
  <Card title="Integration catalog" icon="grid" href="/concepts/catalog">
    Find batch-capable actions and their per-call limits.
  </Card>

  <Card title="Trigger with records" icon="table" href="/guides/trigger-with-records">
    Fan a file of records out into one execution per row.
  </Card>
</CardGroup>
