Skip to main content
Do this when you want to render or follow a run as it happens. Do not build a polling loop against GET /v1/workspaces/{workspace}/agents/{agent_id}/tasks/{task_id} for this — the stream already replays everything from the beginning, so attaching late loses nothing.

Prerequisites

  • A task id and its agent id. See Start a task.
  • An API key.
  • A client that can hold an open HTTP response. curl -N works; curl without -N buffers and looks hung.

Choose an endpoint

Steps

1

Stream live

Each frame is a standard SSE event. The SSE event name and the payload’s event_type are the same dotted string, so a consumer can key on either:
The feed replays the task’s full durable history first, then tails live events, de-duplicating by event_id across the hand-off. It closes after task.completed, task.failed, or task.cancelled, except a task.completed whose data.execution_status is waiting: the task is waiting for a follow-up, and this endpoint keeps streaming its next turns.
2

Know where the stream you start a task with ends

POST .../tasks/ streams the run it starts and ends at that turn’s terminal event, waiting or not, so a request does not hold a connection for the 30-minute follow-up window. Send a follow-up with POST .../tasks/{task_id}/command (queue_message) and read their turns from GET .../tasks/{task_id}/events/stream (reconnect with Last-Event-ID to skip what you have). POST .../tasks/?follow=true keeps the old behaviour: the stream stays open through follow-up turns until the run finishes or 30 minutes pass.
3

Drop token-level chunks

llm.call.chunk events are high volume — one per token. They are included by default. Turn them off when you only need structural progress:
4

Read history instead

A paged event and a live frame carry the same fields in the same places: event_type, event_id, timestamp and the payload under data, shaped the same way. id equals event_id. metadata is a deprecated alias holding the stored payload before the live stream’s flattening of nested data/original_data; read data.Filter with ?event_type=tool.call. Chunks never appear here — they are stream-only and are not persisted.
5

Write the consumer correctly

Two rules keep a consumer from breaking when the vocabulary grows:
  1. Do not switch on every event name. Pass unknown types through. New event types are added without a version bump.
  2. Only three types are terminal: task.completed, task.failed, task.cancelled. Stop on those and nothing else.
To collapse a model call or a tool call into one UI element rather than four lines, group by part id: tool_call_id for tool events, {execution_id}:{iteration} for LLM events. A later event with the same part id replaces the earlier one.

Verify

Confirm the stream terminates on its own with a terminal event rather than hanging:
The command exits as soon as the terminal frame arrives. If it exits with no output, the task produced no terminal event within the feed’s limits — see below.

Troubleshooting

Without -N , curl buffers the response. Add -N . If frames still do not arrive, confirm the task actually dispatched: a task with execution_id: null never started a workflow and will never emit events.
The feed has a 30-minute wall-clock limit so a stuck task does not tail forever. This is the feed giving up, not the task ending. Re-attach, or check GET .../tasks/{task_id} for the current status.
Expected. The reader replays from the database, then attaches to the live tail and waits for a terminal event it may have already replayed. It closes on the replayed terminal event.
The event was published to the live stream but its database write failed; that failure is recorded and does not retry. The live tail is also best-effort in the other direction — a failed publish leaves the event in history only. History is the durable copy; prefer GET .../events when completeness matters.
There is no such event type. A blocked task emits task.failed on the feed while its row reads blocked . Read failure_reason from the task to tell them apart.

Events

The one dotted event vocabulary AgentArea emits, the two delivery paths behind it

Debug a failed task

Read the failure code, narrow it with the task rollup

Start a task

Launch an agent run over REST, the CLI, or A2A
Last modified on October 13, 2026