Streaming events
Follow a session's events and resume after a disconnect.
A session’s log is the whole truth about it, and watching it is optional: a run keeps going whether or not anyone is connected, and when you reconnect the log tells you everything you missed.
Two ways to read
GET /v1/sessions/{session_id}/events # a page at a time
GET /v1/sessions/{session_id}/events/stream # live, over SSE
Both take the session token or a key with Sessions › Read, and both return the same events: one flat JSON object each, with its sequence number, its time, and its type.
{ "seq": 5, "created_at": "2026-09-09T18:49:02Z", "type": "message_assistant", "message": { "role": "assistant", "content": [ ... ] } }
Every type is in the event reference.
Pages
The paged endpoint takes limit (1 to 200, 50 by default), order — desc, newest first, by default, or asc — and a cursor: before_seq or after_seq, each exclusive. The page’s next_cursor is the sequence number to pass back as before_seq when reading newest first, or after_seq when reading oldest first.
The stream
curl -N "https://api.us.cloud.subako.ai/v1/sessions/$SESSION_ID/events/stream?after_seq=0" \
-H "Authorization: Bearer $SUBAKO_SESSION_TOKEN"
id: 0
id: 2
event: message_user
data: {"seq":2,"created_at":"...","type":"message_user","source":"client","message":{...}}
id: 5
event: message_assistant
data: {"seq":5,"created_at":"...","type":"message_assistant","message":{...}}
id: 6
event: run_completed
data: {"seq":6,"created_at":"...","type":"run_completed","run_id":"01a08780-..."}
- With no cursor, the stream starts at the log’s current head: what happens next, and nothing before.
after_seq=0replays the whole log first, then continues live. - The first frame carries only an
id— where the stream starts — so a reconnect has a cursor even if nothing happens. - Each event frame’s
idis its sequence number, andeventits type. - A comment every 15 seconds keeps idle connections open.
- The stream spans the session’s whole life. It does not close when a run ends; watch for the run’s own terminal event.
To show both history and what comes next, open the stream first, then read the pages newest first, and drop any event whose seq you already have.
Resuming after a drop
Reconnect with Last-Event-ID — or after_seq — set to the last sequence number you handled. The stream replays from just after it, then continues live. The header wins when both are given.
curl -N "https://api.us.cloud.subako.ai/v1/sessions/$SESSION_ID/events/stream" \
-H "Authorization: Bearer $SUBAKO_SESSION_TOKEN" \
-H "Last-Event-ID: 42"
- Advance your cursor only after you have handled the event. It is a promise about what you processed, not about what arrived.
- Treat events as replayable. Key your state on
seqrather than appending blindly.
Text as it is written
deltas=true adds delta frames: the model’s text, thinking, and tool-call arguments as they stream, before the finished message_assistant lands. They carry no id, are never stored, and may be lost; the stored message that follows is what counts.
event: delta
data: {"run_id":"...","target":{"type":"model_call","data":{"model_call_id":"..."}},"n":0,"delta":{"type":"append","data":{"parts":[{"type":"text","data":{"index":0,"text":"Order 10"}}]}}}
Parts are text, thinking, or tool_call (whose arguments is a fragment of JSON text). A {"type": "discard"} delta drops what the target streamed so far. A target ends at the message_assistant carrying the same model_call_id.
When a run is over
A run ends in exactly one of run_completed, run_failed (with its error), or run_cancelled, each naming its run_id. A session with no run in flight is idle, not finished: the next input starts another run on the same log.