> ## 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.

# Batches and executions

> How triggers become batches and executions, their status lifecycles, fan-out over records, and recurring schedules

When you trigger a [definition](/concepts/flow-definitions), GetDialed records the run as a [**trigger**](/concepts/triggers) — the immutable audit anchor — plus one **batch** for the triggering event and one or more **executions** for the actual runs. You never create batches or executions directly — they're always produced by a trigger.

## Batches

A batch represents one triggering event and groups every execution it produced. Key fields:

| Field                        | Description                                                                                             |
| ---------------------------- | ------------------------------------------------------------------------------------------------------- |
| `id`                         | The batch's own identifier (e.g. `batch_55667788`)                                                      |
| `definition_id`              | The definition that was triggered                                                                       |
| `trigger_id`                 | The [trigger](/concepts/triggers) that produced this batch — the immutable audit anchor                 |
| `trigger_type`               | The entry point that created the trigger: `api_call`, `webhook`, `schedule`, or `event`                 |
| `trigger_source`             | Optional caller-supplied provenance label (e.g. `salesforce`, `ui`). `null` when not supplied           |
| `status`                     | See the lifecycle table below                                                                           |
| `record_count`               | How many executions this batch fans out to. `1` for a simple trigger, `N` when you trigger with records |
| `input_data`                 | The input object for a single-record trigger                                                            |
| `records`                    | The record array for a multi-record trigger                                                             |
| `metadata`                   | Arbitrary metadata you attached at trigger time                                                         |
| `scheduled_at`               | If set, the batch waits until this time before running                                                  |
| `schedule_id`                | Links back to the [schedule](/concepts/schedules) that fired it, if any                                 |
| `started_at`, `completed_at` | Lifecycle timestamps                                                                                    |

<Note>
  `trigger_type` and `trigger_source` are two independent dimensions. **Type** is the deterministic entry point (`api_call` / `webhook` / `schedule` / `event`); **source** is the free-form caller label you pass at trigger time. Filter or group on either one. See [Triggers](/concepts/triggers) for the full model.
</Note>

### Batch status lifecycle

| Status             | Meaning                                |
| ------------------ | -------------------------------------- |
| `pending`          | Created, not yet started               |
| `scheduled`        | Waiting for its `scheduled_at` time    |
| `running`          | Executions are in flight               |
| `completed`        | Every execution succeeded              |
| `partially_failed` | Some executions succeeded, some failed |
| `failed`           | Executions failed                      |
| `cancelled`        | Cancelled before it ran                |

A batch moves `pending → running → completed | partially_failed | failed`. Passing `scheduled_at` at trigger time inserts a `scheduled` stage before `running`. Only `pending` or `scheduled` batches can be cancelled (`DELETE /batches/{id}`) — anything already running returns `409`.

### Account-wide runs feed

`GET /batches` lists **every** batch across all of your definitions, newest first — the backing feed for a "recent runs" screen. `GET /definitions/{id}/batches` is the same feed scoped to a single definition.

| Parameter       | Type     | Description                                               |
| --------------- | -------- | --------------------------------------------------------- |
| `status`        | enum     | Filter to one batch status.                               |
| `definition_id` | string   | Filter to batches from one definition.                    |
| `source`        | string   | Filter by trigger origin label (e.g. `api`, `schedule`).  |
| `since`         | datetime | Lower bound on `created_at` (`>=`).                       |
| `limit`         | integer  | Page size, 1–500 (default 50).                            |
| `cursor`        | string   | Opaque cursor from the previous response's `next_cursor`. |

The response is the converged **`Page`** envelope — `items`, `total`, `limit`, `skip`, and `next_cursor`. This is a data-family feed, so `total` is always `null`; page forward until `next_cursor` is `null`, which signals no more rows. Only your own account's batches are ever returned.

```json theme={null}
{
  "items": [
    {
      "id": "batch_55667788",
      "status": "completed",
      "trigger_id": "trigger_44556677",
      "trigger_type": "schedule",
      "trigger_source": null
    }
  ],
  "total": null,
  "limit": 50,
  "skip": 0,
  "next_cursor": "eyJjcmVhdGVkX2F0Ijog..."
}
```

## Executions

An execution is one durable run processing one set of inputs. Each belongs to exactly one batch:

| Field                            | Description                                                                                                 |
| -------------------------------- | ----------------------------------------------------------------------------------------------------------- |
| `batch_id`                       | The parent batch                                                                                            |
| `trigger_id`                     | The [trigger](/concepts/triggers) this execution traces back to                                             |
| `trigger_type`, `trigger_source` | Denormalized trigger dimensions, so the executions feed can filter/group by trigger without a second lookup |
| `definition_id`                  | The definition being run                                                                                    |
| `status`                         | See the lifecycle table below                                                                               |
| `input_data`                     | The inputs this specific run received                                                                       |
| `workflow_id`                    | Identifier of the underlying durable run                                                                    |
| `result`                         | The final output when the run completes                                                                     |
| `error`                          | The error message when the run fails                                                                        |
| `started_at`, `completed_at`     | Lifecycle timestamps                                                                                        |

### Execution status lifecycle

| Status      | Meaning                                       |
| ----------- | --------------------------------------------- |
| `pending`   | Created, not yet picked up                    |
| `running`   | Steps are executing                           |
| `completed` | Finished successfully — `result` is populated |
| `failed`    | Finished with an error — `error` is populated |
| `cancelled` | Stopped before completion                     |

An execution moves `pending → running → completed | failed`. List a batch's executions with `GET /batches/{batch_id}/executions` and fetch one with `GET /executions/{id}`.

### Per-step run detail

`GET /executions/{id}` embeds a **per-step breakdown** so a run-detail screen renders in one call — the step list is bounded by the definition's task chain, so there is no pagination and no per-step endpoint.

Two fields carry it:

| Field          | Type          | Description                                                                                                                                                                                        |
| -------------- | ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `steps`        | array \| null | One row per step: `step_name`, `step_index`, `status`, `started_at`, `completed_at`, and structured error fields (`error_type`, `message`) on failures. Never includes step input/output payloads. |
| `steps_source` | enum          | Where `steps` came from — see below.                                                                                                                                                               |

`steps_source` is always set on the detail route so a client can distinguish "this run genuinely has no steps" from "step detail isn't available for this run":

| Value         | Meaning                                                                                                            |
| ------------- | ------------------------------------------------------------------------------------------------------------------ |
| `live`        | Folded from the execution's still-live event stream — the most detailed view.                                      |
| `snapshot`    | A durable per-step summary captured when the run reached its terminal state. Timing and errors only, no payloads.  |
| `unavailable` | No step detail exists — an older run from before this feature, or one whose events have aged out with no snapshot. |

<Note>
  `steps` and `steps_source` are populated only on the single-execution detail route (`GET /executions/{id}`). The executions **list** route leaves them `null` — it isn't the run-detail consumer.
</Note>

## Single-record vs. multi-record triggers

A trigger creates executions in one of two shapes:

* **Single-record** — pass `input_data`. The batch has `record_count: 1` and one execution that receives your `input_data`.
* **Multi-record (fan-out)** — pass a `records` array (up to 15,000,000 records). One execution is created **per record**, each receiving that record as its input. The batch's `record_count` is the array length, and its final status aggregates all children: all succeed → `completed`, all fail → `failed`, a mix → `partially_failed`.

See [triggering with records](/guides/trigger-with-records) for a walkthrough.

## Recurring triggers: schedules

To run a definition on a repeating cadence, create a **schedule** with `POST /schedules`. Each fire creates a normal batch — visible under `GET /definitions/{id}/batches` with `trigger_type: "schedule"` and a `schedule_id` pointing back at the schedule — so the batch and execution lifecycle above applies unchanged.

| Field                               | Description                                                            |
| ----------------------------------- | ---------------------------------------------------------------------- |
| `definition_id`                     | The definition to fire. Immutable after creation                       |
| `description`                       | Human-readable description (1–200 chars)                               |
| `cron_expression`                   | 5-field UTC cron, e.g. `*/15 * * * *`                                  |
| `enabled`                           | Default `true`. Set `false` to pause without losing fire history       |
| `input_data`                        | Input passed to each fired batch                                       |
| `metadata`                          | Free-form metadata                                                     |
| `next_fire_at`                      | Computed from the cron expression at read time                         |
| `last_fired_at`, `last_fire_status` | The most recent fire and its state: `running`, `success`, or `failure` |

<Warning>
  Cron expressions are **UTC-only and 5-field syntax only**. Six-field expressions (with seconds), aliases like `@hourly`, and any `timezone` field are rejected with `422`.
</Warning>

Fire behavior is fixed: if a previous fire is still running when the next tick arrives, that tick is **skipped**, not queued. Missed fires during downtime are replayed only within a 60-second catch-up window.

Manage schedules with `PATCH /schedules/{id}` (change the cron expression, pause/resume with `enabled`, update `input_data`) and `DELETE /schedules/{id}` — deleting a schedule never affects batches it already fired.
