---
title: "Stream trace events · Ataris docs"
description: "Follow a session's events live, resume after a disconnect, or page through stored events."
canonical: "https://ataris.dev/docs/guides/stream-events"
last-updated: 2026-10-12
---
# Stream trace events

Follow a session's events live, resume after a disconnect, or page through stored events.

There are two ways to read a session's trace: follow it live, or page through what's stored.

## Follow a session live

`session.events()` returns an async iterable of [trace events](/docs/concepts/trace-events). It replays from the start, follows the session as it runs, and finishes when the session ends.

```ts
const session = await ataris.sessions.get(sessionId)

for await (const event of session.events({
  onSession: (summary) => console.log("state:", summary.state),
})) {
  if (event.verdict === "blocked" || event.flags.includes("credential")) {
    console.warn("check this:", event.kind, event.summary)
  }
}
```

`onSession` is called whenever the session's state changes, and once at the end. `session.data` is kept up to date as well.

### Reconnects

If the connection drops, the stream reconnects on its own and resumes from the last event it delivered, so you don't see duplicates or gaps. It retries network errors, `5xx`, and `429` responses, starting at `retryDelayMs` (1 second by default) and doubling up to 30 seconds. After `maxRetries` consecutive failures (5 by default) it throws.

### Resume later

Every event has an `id`. Save the last one you processed, and pass it as `after` to pick up where you left off:

```ts
for await (const event of session.events({ after: lastEventId })) {
  // ...
}
```

### Stop listening

Pass an `AbortSignal` to stop reading without stopping the session:

```ts
const controller = new AbortController()
setTimeout(() => controller.abort(), 60_000)

for await (const event of session.events({ signal: controller.signal })) {
  // ...
}
```

## Page through stored events

For a finished session, or a batch job, read pages instead of a stream:

```ts
let after = 0
for (;;) {
  const page = await ataris.sessions.events(sessionId, { after, limit: 500 })
  for (const event of page.events) {
    // ...
  }
  if (!page.hasMore) break
  after = page.nextAfter
}
```

`limit` is 1 to 1,000, and 500 by default.

## Over HTTP

The live stream is Server-Sent Events at `GET /v1/sessions/{id}/stream`. It sends `trace` events (with the event `id` as the SSE id), `session` events on state changes, and an `end` event once the session is over and every event has been sent. Resume with the `Last-Event-ID` header or `?after=`. See the [REST API reference](/docs/reference/api#stream-events).

