---
title: Streaming events
description: 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

```http
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`.

```json
{ "seq": 5, "created_at": "2026-09-09T18:49:02Z", "type": "message_assistant", "message": { "role": "assistant", "content": [ ... ] } }
```

Every type is in the [event reference](/reference/events).

### 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

```bash
curl -N "https://api.us.cloud.subako.ai/v1/sessions/$SESSION_ID/events/stream?after_seq=0" \
  -H "Authorization: Bearer $SUBAKO_SESSION_TOKEN"
```

```txt
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=0` replays 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 `id` is its sequence number, and `event` its 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.

```bash
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 `seq` rather 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.

```txt
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.

:::note
Since the log is the whole truth, you never need to hold a stream open to stay correct. A worker that reads the paged endpoint every few seconds sees exactly what a streaming client sees, a little later.
:::
