Skip to main content
A trigger doesn’t have to carry a single input. Pass a records array — one object per row — and GetDialed fans the flow out: one batch for the trigger, one job per record, and one execution per job, all running concurrently. It’s the natural fit for anything row-shaped: a parsed CSV, a CRM export, a nightly batch of leads.

How fan-out works

When you POST /flows/definitions/{id}/trigger with records:
  1. One batch is created with record_count equal to the number of records.
  2. One job is created per record — the per-record audit point. Each record becomes that job’s input_data.
  3. Each job runs one execution, so your definition’s expressions read the record’s fields via {{input.<column>}} — exactly as they would for a single trigger. All executions run concurrently and succeed or fail independently.
  4. When every execution finishes, the batch gets an aggregate status.
Per-record inputs live on jobs, not on the batch. To read the individual records after triggering, list the batch’s jobs with GET /flows/jobs?batch_id=<batch_id> — the batch object itself only carries the record_count rollup.
A single trigger accepts up to 10,000 inline records. Beyond that, upload a record set and trigger with record_set_id — see Bulk ingestion. An inline array over the cap is rejected with 422.

Example: load a contact list

Say you have a contact CSV:
Your flow reads each row’s columns through {{input.first_name}}, {{input.last_name}}, and {{input.phone}}. Parse the file into one dict per row and send the rows as records:
The definition can’t tell the difference between a single trigger and a fan-out — each execution sees one record as its input_data. Build the flow once, then trigger it with 1 record, 10,000 inline, or millions by way of a record set.

Track the batch and its executions

Fetch the batch to see the aggregate picture — record_count confirms how many executions were fanned out:
A partially_failed batch means some rows made it and some didn’t. List just the failures to find out which:
Each failed execution carries its own input_data (the original record) and an error message — everything you need to fix the row and re-trigger just the failures as a new, smaller records array. To inspect the records themselves — the per-record inputs that used to be embedded on the batch — list the batch’s jobs:
Each job carries the record’s resolved input_data and its per-record status. Filter to just the failures with ?batch_id=batch_55667788&status=failed.

Defer the start with scheduled_at

To load records now but run them later — say, at the start of the calling window — add scheduled_at (ISO 8601):
The trigger response comes back with "status": "scheduled" instead of "pending", and the executions start at the given time. Until then the batch can still be cancelled with DELETE /flows/batches/{batch_id} — only pending and scheduled batches are cancellable.

Record provenance with source and metadata

Both fields are free-form provenance labels you attach at trigger time, so you can answer “where did this run come from?” weeks later. source is filterable on the account-wide runs feed (GET /flows/batches?source=...): Neither affects execution; they exist purely for auditing and filtering on your side.

Trigger request reference

The definition must be active — triggering a draft or archived definition returns 400. See Build your first flow for the create-and-activate walkthrough.
scheduled_start_at (the per-record start time on jobs) is present in the schema so clients can build against the finished shape, but per-record scheduling is not enabled yet. A future scheduled_start_at is rejected with 422 — there is no accept-and-ignore. To defer a whole batch today, use the batch-level scheduled_at above. (This is distinct from scheduled_at, which is supported.)

Next steps

Bulk ingestion

Past 10,000 records — upload a record set in chunks and trigger by reference.

Batches and executions

The full lifecycle and every status in detail.

Batching

Aggregate records across executions into efficient platform calls.