Skip to main content

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

A gated (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 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)

Requires the 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.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:
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 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:
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}/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

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

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 (the correlationKey), status filters, and a waiting for you section for runs suspended on your approval or input. Open a run for:
  • 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