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
failing is a flag, not a status: a step with onError: 'fail' failed and the run is unwinding to failed.
Gates
Agated (or, for exception, suspended) run carries gate.kind:
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
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. 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: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
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
completedAtplus 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 withlua workflows delete-run <runId>.
Streaming events (SSE)
workflows:read scope on the agent. The response is a standard Server-Sent Events stream:
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.failedfilters both the replay and the tail to those types.
Polling form
The same URL withoutAccept: text/event-stream returns one JSON page:
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
- Only a signed-in person can resolve an approval (
workflows:execute); service principals get 403APPROVAL_REQUIRES_HUMAN. - Editing the payload requires the
expectedFingerprintof the version you looked at; 409PAYLOAD_MISMATCHmeans 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.
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 calledctx.suspend(payload) is resumed with data that must match its resumeSchema:
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}/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
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
Starting and listing runs over HTTP
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 (thecorrelationKey), status filters, and a waiting for you section for runs suspended on your approval or input. Open a run for:
Related
- Workflows Command -
start,watch,status,approve,signal,cancel,retry-step,archive-runs - Job tier - what a Job-tier step’s panel and log contain
- Authoring - approval, signal and retry options on the definition

