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

# Runs and Events

> Run lifecycle and statuses, the events ledger, the SSE stream, approvals and signals, cancel and retry, and the desktop Runs pages

## Overview

Every start creates a **run** (`wfr_...`) with a status, a budget, step rows and an append-only **events ledger**. The ledger is what the CLI's `watch`, the desktop's live views and your own SSE clients read; it stays with the run after the run ends.

## Run lifecycle

```
queued ──▶ running ──▶ completed
   │          │  ▲
   │          ├──┼──▶ suspended  (waiting for a person: approval, input, signal)
   │          ├──┼──▶ waiting    (a timer: sleep, retry backoff, a child run)
   │          ├──┼──▶ gated      (holds no slot: consent, quota, billing, budget, exception)
   │          │
   │          └─▶ cancellation_requested ──▶ cancelled / abandoned
   └──▶ gated
                       failed · timed_out
```

| Group     | Statuses                                                     | Meaning                                                                                   |
| --------- | ------------------------------------------------------------ | ----------------------------------------------------------------------------------------- |
| In flight | `queued`, `running`, `cancellation_requested`                | The run holds an execution slot.                                                          |
| Idle      | `gated`, `suspended`, `waiting`                              | The run holds no slot; nothing is executing until a person, a signal or a timer wakes it. |
| Terminal  | `completed`, `failed`, `cancelled`, `abandoned`, `timed_out` | Final. Late writes from a stale worker are fenced off.                                    |

`failing` is a flag, not a status: a step with `onError: 'fail'` failed and the run is unwinding to `failed`.

### Gates

A `gated` (or, for `exception`, `suspended`) run carries `gate.kind`:

| Kind                       | Why                                                                                                    | How it clears                                                   |
| -------------------------- | ------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------- |
| `start-consent`            | The start needs a person's consent                                                                     | Approve on the desktop (`lua workflows start` exits 6)          |
| `quota`                    | The organisation's concurrency or suspended-run cap is full (`code: concurrency_cap \| suspended_cap`) | Automatically when a slot frees                                 |
| `billing`                  | Credits ran out mid-run                                                                                | Top up, then `lua workflows retry-step <runId> --step <stepId>` |
| `budget`                   | The run's own `maxCredits` / `maxSteps` / `jobSeconds` ran out (`dimension`, `spent`, `cap`)           | Raise the budget (desktop, or `Workflows.raiseBudget`)          |
| `exception`                | A step parked for a person (`onError: 'park'`, or an external-effect step hit by a platform fault)     | Retry, skip, complete or fail the step                          |
| `org_archived`, `disabled` | The organisation is archived, or workflows were switched off                                           | Administrative                                                  |

### Triggers

`run.trigger` records how the run started: `chat`, `sdk`, `api` (CLI and HTTP starts), `schedule`, `webhook` (trigger primitives), `template`, `workflow` (a parent run), `device`.

## Step lifecycle

| Status                                     | Meaning                                                                         |
| ------------------------------------------ | ------------------------------------------------------------------------------- |
| `pending`                                  | Upstream steps have not finished.                                               |
| `ready`, `waiting`, `dispatched`           | Eligible; waiting for a slot, a timer, or a worker/Job to claim it.             |
| `claimed`, `running`                       | Executing under a lease.                                                        |
| `suspended`                                | Parked for a person: `suspend.kind` is `approval`, `input`, `signal` or `gate`. |
| `cancellation_requested`                   | Being cancelled.                                                                |
| `completed`, `skipped`                     | Done.                                                                           |
| `failed`, `cancelled`, `timeout`, `reaped` | Terminal failures (`reaped` = the lease expired and the attempt was reclaimed). |

Each row carries `attempt` / `maxAttempts`, `dependsOn`, timings, a 2 KB preview of input and output, `error { code, message, killReason? }`, `artefactIds`, and - for Job-tier rows - the `job` block described in [Job tier](/workflows/job-tier#what-the-run-page-shows). Step kinds are `code`, `agent`, `tool`, `map`, `branch`, `foreach`, `loop`, `sleep`, `sleepUntil`, `approval`, `signal`, `subrun` and `merge`.

## The events ledger

Every change to a run appends one event:

```json theme={null}
{
  "runId": "wfr_3f2...",
  "seq": 12,
  "ts": "2026-09-03T10:14:11.204Z",
  "type": "step.completed",
  "stepId": "plan",
  "attempt": 1,
  "actor": { "kind": "system" },
  "data": { "durationMs": 8120 }
}
```

`seq` is per run and strictly increasing - order by `seq`, never by `ts`. `stepId` is present on every `step.*`, `approval.*`, `signal.*` and `timer.*` event.

### Event types

| Family                     | Types                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |
| -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Run lifecycle              | `run.created`, `run.gated`, `run.gate_cleared`, `run.started`, `run.claimed`, `run.phase`, `run.log`, `run.suspended`, `run.waiting`, `run.resumed`, `run.cancel_requested`, `run.completed`, `run.failed`, `run.cancelled`, `run.abandoned`, `run.timed_out`, `run.failing`, `run.bailing`, `run.repaired`, `run.reconciled`, `run.signal_parked`, `run.billing_hold`, `run.budget_parked`, `run.budget_raised`, `run.exported`, `run.reassigned`, `run.reply_delivered`, `run.reply_failed`, `run.workspace_provisioned`, `run.workspace_released`, `run.workspace_restored`, `run.migrated`, `run.migration_scheduled`, `run.migration_applied`, `run.migration_refused`, `run.callback_delivered`, `run.callback_failed`, `run.export_auto_queued`, `run.export_pushed`, `run.export_push_failed` |
| Step lifecycle             | `step.ready`, `step.dispatched`, `step.claimed`, `step.started`, `step.progress`, `step.completed`, `step.failed`, `step.skipped`, `step.cancelled`, `step.timeout`, `step.reaped`, `step.retried`, `step.retry_scheduled`, `step.throttled`, `step.suspended`, `step.resumed`, `step.parked`, `step.resolved`, `step.gated`, `step.signal_received`, `step.approval_item_decided`, `step.artefact`, `step.dataset`, `step.checkpointed`, `step.job_spawned`, `step.merged`, `step.mcp_call_denied`, `step.foreach_expanded`, `step.loop_iteration`                                                                                                                                                                                                                                                   |
| Approvals, signals, timers | `approval.requested`, `approval.edited`, `approval.escalated`, `approval.expired`, `approval.resolved`, `signal.received`, `signal.consumed`, `signal.rejected`, `timer.set`, `timer.fired`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |
| Billing and other          | `billing.gate`, `billing.step_charged`, `billing.budget_exhausted`, `journal.divergence`, `goal.updated`, plus schedule and fleet events                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              |

`step.progress` carries `data.message` from `ctx.log(...)`. `run.suspended` carries `{ stepId, kind }`; `approval.requested` carries the `approvalId` you need to resolve it from the API or CLI.

### Retention

* A run and its events, steps, signals and artefacts are kept together. A run is stamped to expire 365 days after it was created; when it ends, the expiry is restamped to `completedAt` plus the organisation's workflow retention (7..90 days; the platform default is 90 and the ceiling is 90). Lowering the organisation's retention tightens existing runs; raising it never extends a run already stamped.
* Export before expiry with `lua workflows archive-runs --since 8d --out ./archive` (bundles are downloadable for 7 days after the export is requested), or delete a terminal run early with `lua workflows delete-run <runId>`.

## Streaming events (SSE)

```
GET /workflows/{agentId}/runs/{runId}/events
Accept: text/event-stream
Authorization: Bearer <token>
```

Requires the `workflows:read` scope on the agent. The response is a standard Server-Sent Events stream:

```
retry: 3000
: connected run=wfr_3f2... from=0 head=12

id: 11
event: step.started
data: {"runId":"wfr_3f2...","seq":11,"ts":"...","type":"step.started","stepId":"plan", ...}

id: 12
event: step.completed
data: {...}

event: heartbeat
data: {"ts":1756894451204,"head":12}

id: 13
event: run.completed
data: {...}

event: end
data: {"seq":13,"status":"completed"}
```

| Frame                                            | Meaning                                                                                                                                                |
| ------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `id: <seq>` / `event: <type>` / `data: <event>`  | One ledger event. Missed history is replayed from your cursor before live events are tailed.                                                           |
| `heartbeat` (no id)                              | Every 15 s while the stream is idle.                                                                                                                   |
| `end { seq, status }`                            | Sent after the run's terminal event; the server closes the stream.                                                                                     |
| `reconnect { reason }`                           | Reconnect from your last id.                                                                                                                           |
| `error { code }`                                 | For example `run_gone` (the run was erased) or `forbidden` (your access was re-checked and revoked).                                                   |
| `workflow.output.acl_changed { restricted, ts }` | Output visibility flipped for you mid-stream; payloads are redacted or unredacted from here on. This control frame has no id and is not in the ledger. |

### Cursors

* `Last-Event-ID: <seq>` on reconnect resumes after that sequence. It **wins** over the query parameter.
* `?afterSeq=<seq>` does the same for a first connection.
* `?types=step.completed,run.failed` filters both the replay and the tail to those types.

### Polling form

The same URL without `Accept: text/event-stream` returns one JSON page:

```
GET /workflows/{agentId}/runs/{runId}/events?afterSeq=0&limit=500
→ { "events": [ ... ] }
```

`lua workflows logs` uses this form; `lua workflows watch` uses the stream and reconnects from `Last-Event-ID` when it drops.

### Redaction

Event payloads (`data`) carry step inputs and outputs. A viewer without the `workflows:read-outputs` grant (or outside a workflow's `outputVisibility`) receives the same frames with `data` redacted; the check is repeated every 60 s on a live stream.

## Approvals and signals

### Resolving an approval

```
POST /workflows/{agentId}/runs/{runId}/approvals/{approvalId}/resolve
{ "decision": "approve" | "deny", "note"?: "...", "editedPayload"?: {...}, "expectedFingerprint"?: "..." }
```

* Only a signed-in person can resolve an approval (`workflows:execute`); service principals get 403 `APPROVAL_REQUIRES_HUMAN`.
* Editing the payload requires the `expectedFingerprint` of the version you looked at; 409 `PAYLOAD_MISMATCH` means it changed - refetch and try again.
* Other refusals: `NOT_AN_APPROVER`, `FOUR_EYES_REQUIRED`, `STEP_UP_REQUIRED` (approve from the desktop with a fresh login), `EDIT_NOT_ALLOWED`.
* A second resolve is a 200 no-op `{ resolved: false, outcome: 'noop', reason }` naming who decided.

The `approvalId` (`wfa_...`) is on the run's `suspensions[].suspend.approvalId`, on the `approval.requested` event, and on the desktop card. CLI: `lua workflows approve <runId> --approval <id> --decision approve|deny [--note] [--edit @file --fingerprint <f>]`.

### Resuming an input suspend

A step that called `ctx.suspend(payload)` is resumed with data that must match its `resumeSchema`:

```
POST /workflows/{agentId}/runs/{runId}/steps/{stepId}/resume
{ "resumeData": {...} }
```

CLI: `lua workflows resume <runId> --step <id> --data '{...}'`. Resuming an approval step or a signal wait this way is refused with 409 `APPROVAL_REQUIRES_HUMAN` / `USE_SIGNAL_ROUTE`.

### Delivering a signal

```
POST /workflows/{agentId}/runs/{runId}/signals/{name}
{ "payload"?: {...}, "dedupeKey"?: "..." }
```

| Response                                                 | Meaning                                            |
| -------------------------------------------------------- | -------------------------------------------------- |
| 202 `{ accepted: true, consumed: true, stepId }`         | A waiting step took it.                            |
| 202 `{ accepted: true, consumed: false }`                | Parked until a step waits for it.                  |
| 200 `{ accepted: true, duplicate: true }`                | The `dedupeKey` was seen before.                   |
| 202 `{ accepted: false, reason: 'source_not_accepted' }` | The wait's `acceptedSources` excludes this caller. |
| 409 `RUN_TERMINAL`                                       | The run already ended.                             |

`POST /workflows/{agentId}/signals/by-key` with `{ correlationKey, name, payload?, dedupeKey?, allowMultiple? }` delivers to the live run(s) started with that `correlationKey` (409 `CORRELATION_KEY_AMBIGUOUS` when several match and `allowMultiple` is false). CLI: `lua workflows signal <runId> <name> --payload '{...}' [--dedupe-key <key>]`.

## Cancelling

```
POST /workflows/{agentId}/runs/{runId}/cancel   { "mode"?: "request" | "force", "reason"?: "..." }
```

A cancel is a request: running steps are asked to stop at their next boundary and the run moves to `cancellation_requested`. The verdict tells you what to do next - `nextAction: 'none'` (done), `'cancel_again'` (ask again to escalate) or `'force'` (a force cancel becomes available at `forceAvailableAt`). `lua workflows cancel <runId> [--reason <text>] [--force]` prints the same.

## Retrying and repairing

| Action                | When                                                                                              | How                                                                                                                                                                |
| --------------------- | ------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `retry-step`          | A step failed past its retries, parked on an exception gate, or is on a billing hold              | `lua workflows retry-step <runId> --step <id> [--note <text>]` - re-arms the same step; 409 `STEP_NOT_PARKED` if it is not parked, `RUN_TERMINAL` if the run ended |
| Resolve a parked step | You want to skip it, supply its output, or fail it                                                | The desktop's exception card, or `POST .../steps/{stepId}/resolve`                                                                                                 |
| Repair run            | The run is terminal and you want to continue from the failed step with the completed steps seeded | The desktop's **Repair** dialog, or `POST .../runs/{runId}/repair`; the new run shares the original's `lineageId`, so `ctx.once` keys still hold                   |
| Raise budget          | The run parked on a `budget` gate                                                                 | The desktop, or `POST .../runs/{runId}/budget`                                                                                                                     |

## Starting and listing runs over HTTP

| Route                                                                         | Scope                                | Notes                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| ----------------------------------------------------------------------------- | ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `GET /workflows/{agentId}/definitions`                                        | `workflows:read`                     | Workflow definitions on the agent (`items`, `nextCursor`).                                                                                                                                                                                                                                                                                                                                                                                              |
| `POST /workflows/{agentId}/runs`                                              | `workflows:execute`                  | Start a run: `{ workflowId, input, idempotencyKey?, budget?, correlationKey?, tags?, waitSeconds?, workflowVersionId? }`. 202 `{ runId, status: 'queued' \| 'gated', watchHint, idempotentReplay? }`; 200 with the run when `waitSeconds` (0..55) elapsed on a settled run. An `Idempotency-Key` header wins over the body field. 409 `RUNS_IN_FLIGHT` under `concurrencyPolicy: 'forbid'`; 429 `CONCURRENCY_CAP` / `SUSPENDED_CAP` with `Retry-After`. |
| `POST /workflows/{agentId}/runs/batch`                                        | `workflows:execute`                  | Many starts at once; each item needs its own `idempotencyKey`. Body at most 2 MB.                                                                                                                                                                                                                                                                                                                                                                       |
| `GET /workflows/{agentId}/runs`                                               | `workflows:read`                     | Filter by `workflowId`, `status`, `correlationKey`, `tag`, `since`; `sort` one of `-createdAt`, `createdAt`, `-durationMs`, `durationMs`; cursor paging.                                                                                                                                                                                                                                                                                                |
| `GET /workflows/{agentId}/runs/{runId}?fields=summary\|full`                  | `workflows:read`                     | `full` hydrates inputs and outputs for readers who may see them.                                                                                                                                                                                                                                                                                                                                                                                        |
| `GET /workflows/{agentId}/runs/{runId}/steps/{stepId}?attempt=n`              | `workflows:read`                     | One step with its attempts (input/output are `{__hidden}` without `read-outputs`).                                                                                                                                                                                                                                                                                                                                                                      |
| `DELETE /workflows/{agentId}/runs/{runId}`                                    | `workflows:execute`                  | Erase a terminal run (202 `{ purgeId }`; 409 `RUN_NOT_TERMINAL`). Creator or org admin, person only.                                                                                                                                                                                                                                                                                                                                                    |
| `POST /workflows/{agentId}/runs/{runId}/export` / `GET .../export/{exportId}` | `workflows:read` + `read-outputs`    | Request and download an evidence bundle of a terminal run.                                                                                                                                                                                                                                                                                                                                                                                              |
| `GET /workflows/{agentId}/runs/{runId}/artefacts/{artefactId}`                | `workflows:read` + `read-outputs`    | A presigned download of a run artefact.                                                                                                                                                                                                                                                                                                                                                                                                                 |
| `GET/POST/PATCH/DELETE /workflows/{agentId}/schedules[/{jobId}]`              | `workflows:read` / `workflows:write` | Schedules attached to a workflow; `PATCH` pauses/resumes and backfills.                                                                                                                                                                                                                                                                                                                                                                                 |

Run inputs are at most 256 KB; a run holds up to 10 tags.

## The desktop Runs pages

**Runs** lists every run on the agent with search by ticket, order or run id (the `correlationKey`), status filters, and a **waiting for you** section for runs suspended on your approval or input. Open a run for:

| Area              | What it shows                                                                                                                                                                                                  |
| ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Header and status | Workflow, version, trigger, status pill, budget spent versus cap, and a gate banner when the run is parked (with the action that clears it).                                                                   |
| **Needs you**     | The approval card, resume-input form or exception card for every step waiting on a person. Approve, deny or edit here.                                                                                         |
| **Timeline** tab  | Every step in dispatch order with status, attempt, duration and - for live Job-tier steps - the current phase and activity. Select a step for its input, output, error, attempts, artefacts and Job panel.     |
| **Graph** tab     | The workflow graph with each node coloured by its status (script-form runs show a **Phases** tree instead).                                                                                                    |
| **Events** tab    | The events ledger, live.                                                                                                                                                                                       |
| **Log** tab       | Tool calls (with input and output), the agent's notes and step events in one stream, with **All / Tool calls / Agent notes / Step events** filters and a **Follow** switch that keeps the newest line in view. |
| Aside             | Run details (ids, correlation key, tags, timings, last activity), the workspace panel for Job-tier runs, and the actions rail: cancel, retry, repair, release workspace, export.                               |

## Related

* [Workflows Command](/cli/workflows-command) - `start`, `watch`, `status`, `approve`, `signal`, `cancel`, `retry-step`, `archive-runs`
* [Job tier](/workflows/job-tier) - what a Job-tier step's panel and log contain
* [Authoring](/workflows/authoring#approvals) - approval, signal and retry options on the definition
