Streaming & Real-time
Track run progress with polling, event logs, and optional SSE streaming.
Developers building live run progress.
Track run progress with polling, event logs, and optional SSE streaming. For server-side integrations, polling is the recommended default: it is simple, durable, and works through any HTTP client.
Recommended pattern#
- POST /runs → capture
run.idandstatusUrlfrom the 202 response. - Poll GET /runs/{id} every 1-3s until terminal status (
completed,failed,cancelled). - Read outputs from the completed run response.
Event log#
GET /runs/{id}/events returns ordered pages of lifecycle events. Continue with afterSequence when hasMore is true. Use it for step-level progress or debugging.
The first durable acceptance marker is run.queued. It has the normal event envelope (type, runId, sequence, occurredAt, and status: "pending") and is written before preparation begins. run.preparing may follow while the graph is frozen. run.started is emitted only after the execution worker accepts the run. Sequence numbers define the ordering; clients must not infer order from wall-clock polling time.
See List Run Events for the full reference.
Event content#
Each event carries a data object with the content for its type. It is an allowlist with three fields:
text: an incremental chunk of a node's output, onnode.output.delta.status: the run status this event implies, on run-level events.reason: why a run failed, why it is suspended (wait_signal,wait_timer), or why a node was skipped (condition_false,upstream_failed).
Concatenate data.text in sequence order, grouped by nodeId, to reproduce a node's output as it is generated. Anything outside the three fields above (cost records, payer identity, credential names, raw provider errors) is dropped before the event leaves the server, and failure text is generalized the same way as Run.reason.
Event types are an open set. Ignore types you do not recognize rather than treating them as errors.
Live streaming#
Run creation returns streamUrl. Connect with the same Bearer API key; the key needs run:read. The stream begins with one canonical snapshot, replays durable events, then follows sanitized node events and canonical run.status events. Internal account, billing, provider, and raw compute fields are never included. Heartbeat comments arrive every 15 seconds while idle.
Resume after a disconnect with Last-Event-ID. If your SSE client cannot set that header, pass afterSequence=<last_id>. Event ids increase monotonically, replay and live tailing do not duplicate an event, and an already-terminal run sends its snapshot and closes cleanly.
Stopping a run#
POST /runs/{id}/cancel ends a run that is still in flight. The run moves to cancelled, a run.cancelled webhook fires, and the buyer is not charged. The call needs run:execute and is idempotent, so cancelling an already-terminal run returns its existing status with cancelRequested: false. See Cancel Run.
Was this useful?
Your response helps us improve the documentation.