Skip to main content
Returns what happened in a session since a cursor. Long-polls when nothing is new.

Request

Response

With approval you do not have to scan events to find what is blocking the task:
Each assistant message arrives once, as a start, one delta with the whole message, and an end. A slow poller still sees every message exactly once. A new message from you starts a new turn and resets the cursor; the response then reports the new turn only.

The loop

  1. POST /message with wait: false.
  2. GET /events?sessionId=...&wait=30.
  3. Act on status: running, poll again with since=cursor; needs_approval, see Approvals; idle, read text; error, read the error event.
Keep wait at 30 to 60 seconds. It is capped at 60 so a single call never approaches the two-minute timeout most tool runners apply. The request is held while status is running or needs_approval, so a caller that handed a link-only approval to a human can keep long-polling and is woken when the agent resumes. On idle and error it returns at once.

What a caller can see

There are no screenshots on this API. The record of a task is:
  • one data-progress line per step, written by the agent for a human (the tool call’s title),
  • the first line of each console message the step printed,
  • the final assistant text.
Sai describes what is on the screen in those lines and in text. If you need to see the computer, open the session in the Sai app.

Errors

GET /events is available from October 1. Today’s staging has only the SSE path.

Event vocabulary

Both the poll and the stream use these types.

text-delta

Final assistant text. In the poll, one delta carries a whole message; in the stream, concatenate deltas with the same id.

reasoning-delta

Mid-turn narration: what Sai is about to do. Show it as progress, not as the answer.

data-progress

A progress line from a running tool. text comes from one of three places:
  • the tool call’s title: one line the agent wrote for a human, emitted when the call starts;
  • the first line of each console message in the tool’s result, with accessibility-tree dumps and element-reference listings filtered out, each line capped at 300 characters;
  • progressLines on a result from the desktop agent (your own computer), passed through as written.

tool-input-start

A tool call began. The stream adds toolMetadata; the poll does not.

tool-input-available

Follows tool-input-start with the call’s input. For the cloud agent’s execute tool it is { title, code }; code is cut at 4000 characters with a note of how much was dropped. On the stream, input is null.

tool-output-error

A tool call failed. Sai usually recovers; the task is not over. errorText is the result’s error string, capped at 500 characters, or the last error-level console line when there is none.
When the failure is one of the two exhaustion cases below, the event also carries code (and resetsAt for credits):

data-approval-request

Sai is waiting for permission. Check isLinkOnly. Full field list on Approvals.

finish

The turn is complete.

error

The turn failed.
When the account ran out of credits, the event carries code and resetsAt:

Error codes

code appears on error and tool-output-error only for these two cases. Everything else has errorText alone. On either code, stop retrying: the same request will fail again today. GET /account shows the balance and plan behind either error. See Billing and limits.

Framing

Text and reasoning are bracketed by start and end markers on both paths.

Stream-only frames

The SSE path also emits these. They carry nothing the poll lacks.
The stream ends with the literal line data: [DONE].