> ## Documentation Index
> Fetch the complete documentation index at: https://docs.labelbox.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Events

> Send messages to a session, read its transcript, stream live events without losing any, and watch many sessions at once.

Everything that happens in a session is recorded as an ordered event: your messages, the agent's replies, tool calls, status changes, and grader verdicts. This page covers sending events, reading them, streaming them live, and recovering a dropped stream.

The TypeScript samples assume the `rl` client from [Client setup](/managed-agents/api#client-setup).

## Before you begin

* You need the organization developer or admin role to send messages. The organization user role can read and stream events. The billing role cannot see sessions. See [Organizations and roles](/managed-agents/organizations-and-roles).
* For API calls, create a key under **API keys**. See [API keys](/managed-agents/api-keys).
* You need a session id. See [Sessions](/managed-agents/sessions).

## Send messages

`sendSessionEvents` adds turns to a session. Send `message` for one plain-text turn, or `events` for typed turns. If you send both, `events` wins and `message` and the top-level `actor` are ignored.

<Tabs>
  <Tab title="Console">
    Open the session, type in **Message the agent…**, and click **Send**. While the agent is working the button reads **Queue**, and **Interrupt & send** appears beside it.
  </Tab>

  <Tab title="TypeScript">
    ```typescript theme={"theme":"css-variables"}
    const sent = await rl.managedAgents.sendSessionEvents({
      session_id: 'e3a91f5c-7d24-4b68-9c10-2f8e6b4d7a53',
      body: {
        events: [
          { type: 'user.message', text: 'Use the incident tracker as the source of truth.' },
          {
            type: 'system.message',
            content: [{ type: 'text', text: 'Reply in British English.' }],
          },
        ],
      },
    });
    ```
  </Tab>

  <Tab title="cURL">
    ```bash theme={"theme":"css-variables"}
    curl -X POST 'https://api.recursion.labelbox.com/managed-agents/v1/sessions/e3a91f5c-7d24-4b68-9c10-2f8e6b4d7a53/events' \
      -H "Authorization: Bearer $RECURSION_API_KEY" \
      -H 'Content-Type: application/json' \
      -d '{
        "events": [
          {"type": "user.message", "text": "Use the incident tracker as the source of truth."},
          {"type": "system.message", "content": [{"type": "text", "text": "Reply in British English."}]}
        ]
      }'
    ```
  </Tab>
</Tabs>

```json theme={"theme":"css-variables"}
{ "ok": true, "delivery_state": "queued", "events_accepted": 2 }
```

| Event field | Description                                                                                                            |
| ----------- | ---------------------------------------------------------------------------------------------------------------------- |
| `type`      | `user.message` (the default), `system.message`, or `handoff_resolved`. Unknown values are dropped.                     |
| `text`      | Shorthand for one text block. Ignored when `content` is set.                                                           |
| `content`   | Content blocks, such as `[{"type": "text", "text": "..."}]`. Wins over `text`. A `system.message` uses only `content`. |
| `actor`     | Who a `user.message` is attributed to. Defaults to `human:api`. A `system.message` ignores it.                         |

The top-level `referenced_session_ids` grants the session read access to up to 10 earlier sessions from this turn on. If you cannot read one of them, the whole request fails with `404` and no message is delivered. See [Referenced sessions](/managed-agents/referenced-sessions).

A `user.message` with neither `text` nor `content` is dropped without an error. The request fails with `400` only when every entry is dropped. Compare `events_accepted` with the number of events you sent.

A top-level `image` or `document` block can name a file from your library instead of carrying bytes. See [Show a file to the agent in a message](/managed-agents/files#show-a-file-to-the-agent-in-a-message).

### Hand a browser step back

When a session is `awaiting_human`, the agent is waiting for a person to handle a step in its browser display, such as a sign-in. Its `active_handoff` names the request. Send a `handoff_resolved` event with that `handoff_id` to let the agent continue, with an optional `note` of up to 2,000 characters telling it what you did. It must be the only event in the request.

```bash theme={"theme":"css-variables"}
curl -X POST 'https://api.recursion.labelbox.com/managed-agents/v1/sessions/e3a91f5c-7d24-4b68-9c10-2f8e6b4d7a53/events' \
  -H "Authorization: Bearer $RECURSION_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"events": [{"type": "handoff_resolved", "handoff_id": "7c2e9a41-5b38-4d06-a1f7-3e8c6b2d9f50", "note": "Signed in to the vendor portal."}]}'
```

A `handoff_id` that doesn't match the active hand-off, or a session with none, returns `409 conflict`. Without this event, the agent resumes on its own when the hand-off deadline passes.

### What the response means

The response confirms that your turns were stored. It does not include the agent's reply; read that from the events or the stream.

| `delivery_state` | Meaning                                                                                                                                                                                                                                                                     |
| ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `queued`         | The agent is mid-turn. It reads your message at the end of the current turn. [Interrupt](/managed-agents/sessions) to have it read now. Until then the message appears in pending inputs, not the transcript. See [Session operations](/managed-agents/session-operations). |
| `signaled`       | The session was waiting, and your message woke it.                                                                                                                                                                                                                          |
| `resumed`        | The session was idle or finished, and your message started a new run. Only root sessions resume.                                                                                                                                                                            |
| `stored`         | The message was saved, but nothing is running to read it yet.                                                                                                                                                                                                               |

A message to a session that is still being cancelled returns a retryable `409 conflict`. A message to a subagent session in a terminal status also returns `409 conflict`, because only root sessions resume.

## List events

`listSessionEvents` returns events oldest first, in pages. Any session id in a tree returns the combined timeline of the whole tree, including subagents.

<Tabs>
  <Tab title="Console">
    Open the session. The transcript shows every event, and the **Events** tab lists them with their raw fields.
  </Tab>

  <Tab title="TypeScript">
    ```typescript theme={"theme":"css-variables"}
    const events = [];
    let after: string | undefined;
    for (;;) {
      const page = await rl.managedAgents.listSessionEvents({
        session_id: 'e3a91f5c-7d24-4b68-9c10-2f8e6b4d7a53',
        after_event_id: after,
        limit: 200,
      });
      events.push(...(page.events ?? []));
      if (!page.next_page_token) break;
      after = page.next_page_token;
    }
    ```
  </Tab>

  <Tab title="cURL">
    ```bash theme={"theme":"css-variables"}
    curl 'https://api.recursion.labelbox.com/managed-agents/v1/sessions/e3a91f5c-7d24-4b68-9c10-2f8e6b4d7a53/events?limit=200' \
      -H "Authorization: Bearer $RECURSION_API_KEY"
    ```
  </Tab>
</Tabs>

```json theme={"theme":"css-variables"}
{
  "events": [
    {
      "event_id": "01997a3c-5e21-7b4d-8f10-3c6a2e9d4b71",
      "event_type": "message",
      "actor": "human:api",
      "role": "user",
      "session_id": "e3a91f5c-7d24-4b68-9c10-2f8e6b4d7a53",
      "content": {
        "blocks": [{ "type": "text", "text": "Use the incident tracker as the source of truth." }]
      },
      "event_status": "completed",
      "created_at": "2026-09-25T14:05:02Z"
    }
  ],
  "next_page_token": "<id of the last event on this page>"
}
```

| Parameter        | Description                                                                                                                                                                        |
| ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `after_event_id` | Return events after this id, not including it. Must be a lowercase event id; a malformed value returns `400`.                                                                      |
| `limit`          | Events per page. Default 100. A page also stops at about 8 MiB of data, so it can hold fewer events than `limit`.                                                                  |
| `hydrate`        | `images` inlines image data in image blocks.                                                                                                                                       |
| `image_urls`     | `signed` adds a short-lived `url` and `url_expires_at` to image blocks. A URL can be left out when many images are signed at once; fetch those with [Get an image](#get-an-image). |
| `payloads`       | `refs` returns large tool payloads as references instead of inline, which keeps pages small.                                                                                       |

Any other value for `hydrate`, `image_urls`, or `payloads` returns `400`.

**Pagination.** `next_page_token` is always present and equals the id of the last event on the page. Pass it as `after_event_id` for the next page. An empty token means you have read everything that exists right now. A short page with a non-empty token does not mean the end. `events` can be `null` on the last page.

**Event ids.** Event ids sort in the order events were created, so an id works as a position. An `after_event_id` that is well formed but not in this session is not an error: you get the events that sort after it.

**Oversized events.** An event too large for a page is returned alone, with `content_hydration_status: oversized`. Fetch its full content with [Get full event content](#get-full-event-content).

### Event fields

| Field                                           | Description                                                                                                                                                                                                                                                  |
| ----------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `event_id`                                      | Unique, time-ordered id. Use it as a cursor and to deduplicate.                                                                                                                                                                                              |
| `event_type`                                    | `message`, `tool_invocation`, `tool_result`, `approval_request`, `approval_decision`, `summary`, `plan_update`, `session_status`, `artifact`, `advisor_intervention`, `outcome_evaluation`, `board_update`, `thread_context_compacted`, or `turn_committed`. |
| `actor`, `role`                                 | Who produced the event. `role` is `user`, `assistant`, or `system` on messages.                                                                                                                                                                              |
| `content`                                       | `blocks`, plus `stop_reason`, `usage`, `plan`, and `metadata` where they apply.                                                                                                                                                                              |
| `event_status`                                  | `pending`, `completed`, `warning`, `error`, or a lifecycle value on `session_status` events.                                                                                                                                                                 |
| `session_id`, `thread_id`                       | Which session and thread produced the event. See [Multi-agent sessions](/managed-agents/multi-agent).                                                                                                                                                        |
| `tool_name`, `tool_use_id`                      | On tool calls and results. A result shares `tool_use_id` with its call.                                                                                                                                                                                      |
| `parent_event_id`, `causal_event_id`, `turn_id` | Links to related events and the turn.                                                                                                                                                                                                                        |
| `input_tokens`, `output_tokens`, `cost_micros`  | Usage on model events. See [Usage and cost](/managed-agents/usage-and-cost).                                                                                                                                                                                 |
| `content_hydration_status`                      | Set when content was not returned in full.                                                                                                                                                                                                                   |
| `created_at`                                    | When the event was recorded.                                                                                                                                                                                                                                 |

## Get full event content

`getSessionEventContent` returns the full `content` of one event. Use it for oversized events or payload references. The event must belong to the session.

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={"theme":"css-variables"}
    const content = await rl.managedAgents.getSessionEventContent({
      session_id: 'e3a91f5c-7d24-4b68-9c10-2f8e6b4d7a53',
      event_id: '01997a3c-5e21-7b4d-8f10-3c6a2e9d4b71',
    });
    ```
  </Tab>

  <Tab title="cURL">
    ```bash theme={"theme":"css-variables"}
    curl 'https://api.recursion.labelbox.com/managed-agents/v1/sessions/e3a91f5c-7d24-4b68-9c10-2f8e6b4d7a53/events/01997a3c-5e21-7b4d-8f10-3c6a2e9d4b71/content' \
      -H "Authorization: Bearer $RECURSION_API_KEY"
    ```
  </Tab>
</Tabs>

The response carries an `ETag`.

## Get an image

Image blocks in events carry an image URI. `getSessionImage` returns the image bytes for it, with the image's media type. It has no SDK method; call it with `fetch` or cURL. The URI must come from an event in the same session tree.

<Tabs>
  <Tab title="Console">
    Open the session. Images show inline in the transcript.
  </Tab>

  <Tab title="TypeScript">
    ```typescript theme={"theme":"css-variables"}
    const imageUri = '<the image URI from the event>';
    const response = await fetch(
      `https://api.recursion.labelbox.com/managed-agents/v1/sessions/e3a91f5c-7d24-4b68-9c10-2f8e6b4d7a53/images?uri=${encodeURIComponent(imageUri)}`,
      { headers: { Authorization: `Bearer ${process.env.RECURSION_API_KEY}` } },
    );
    const bytes = new Uint8Array(await response.arrayBuffer());
    ```
  </Tab>

  <Tab title="cURL">
    ```bash theme={"theme":"css-variables"}
    curl -G 'https://api.recursion.labelbox.com/managed-agents/v1/sessions/e3a91f5c-7d24-4b68-9c10-2f8e6b4d7a53/images' \
      --data-urlencode 'uri=<the image URI from the event>' \
      -H "Authorization: Bearer $RECURSION_API_KEY" \
      -o image.png
    ```
  </Tab>
</Tabs>

To skip this call, list events with `image_urls=signed` and download from each block's short-lived `url`.

## Stream events

`streamSessionEvents` is a server-sent events (SSE) stream of a session's events as they are recorded. It replays everything after your cursor first, then stays open for new events. The stream closes on its own only when the session reaches a terminal status: `completed`, `failed`, or `cancelled`. An interrupted or sleeping session keeps it open.

<Tabs>
  <Tab title="Console">
    Open the session. The transcript streams live.
  </Tab>

  <Tab title="TypeScript">
    The TypeScript SDK exports a streaming helper, `streamManagedAgentSessionEvents`.

    ```typescript theme={"theme":"css-variables"}
    import { streamManagedAgentSessionEvents } from '@labelbox/recursion-sdk';

    let lastEventId: string | undefined;
    for await (const frame of streamManagedAgentSessionEvents(
      { apiKey: process.env.RECURSION_API_KEY!, baseUrl: 'https://api.recursion.labelbox.com' },
      { sessionId: 'e3a91f5c-7d24-4b68-9c10-2f8e6b4d7a53', afterEventId: lastEventId },
    )) {
      if (frame.type === 'agent.message') console.log(frame.content);
      if (frame.id) lastEventId = frame.id;
    }
    ```

    The helper skips comment frames and does not reconnect by itself. It throws `ManagedAgentSessionStreamError`, with `status` and `code`, if the request fails before any frame arrives.
  </Tab>

  <Tab title="cURL">
    ```bash theme={"theme":"css-variables"}
    curl -N 'https://api.recursion.labelbox.com/managed-agents/v1/sessions/e3a91f5c-7d24-4b68-9c10-2f8e6b4d7a53/events/stream' \
      -H "Authorization: Bearer $RECURSION_API_KEY"
    ```
  </Tab>
</Tabs>

```text theme={"theme":"css-variables"}
: connected

event: agent.message
id: 01997a3d-0b82-7c15-9a43-6e1f2d8c5b09
data: {"type":"agent.message","id":"01997a3d-0b82-7c15-9a43-6e1f2d8c5b09","session_id":"e3a91f5c-7d24-4b68-9c10-2f8e6b4d7a53","processed_at":"2026-09-25T14:05:40Z","content":[{"type":"text","text":"Found 12 open incidents."}],"raw_event":{"event_type":"message"}}

: ping
```

| Parameter              | Description                                                                                                                                                              |
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `after_event_id`       | Start after this event id. Takes precedence over the `Last-Event-ID` header.                                                                                             |
| `Last-Event-ID` header | Start after this event id when `after_event_id` is not set. Standard SSE clients send it on reconnect.                                                                   |
| `types`                | Only send these frame types, comma-separated (`types=agent.message,session.status_idle`) or repeated as `types[]`. Filtered-out events still move your position forward. |

### Frames

Each event is sent as `event: <type>`, `id: <event_id>`, and `data: <json>`. The JSON has `type`, `id`, `session_id`, `session_thread_id`, `processed_at`, `content`, `stop_reason`, `usage`, `error`, `anchor_event_id`, and `raw_event`, which holds the full stored event.

| Frame `type`                                                                     | Sent for                                                                                                                                    |
| -------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| `user.message`, `agent.message`, `system.message`                                | Messages, by role.                                                                                                                          |
| `agent.tool_use`, `agent.tool_result`                                            | Tool calls and their results.                                                                                                               |
| `agent.approval_request`, `user.tool_confirmation`                               | Approval requests and decisions.                                                                                                            |
| `agent.artifact`                                                                 | A saved deliverable.                                                                                                                        |
| `agent.thread_context_compacted`                                                 | A thread's context was compacted.                                                                                                           |
| `session.status_provisioning`, `session.status_queued`, `session.status_running` | The session entered that state.                                                                                                             |
| `session.status_idle`                                                            | The session went idle or completed.                                                                                                         |
| `session.status_terminated`                                                      | The session failed or was cancelled.                                                                                                        |
| `session.status`                                                                 | Any other status change.                                                                                                                    |
| `session.event`                                                                  | Everything else: summaries, plan updates, turn commits, advisor notes, outcome evaluations, and board updates. Read `raw_event.event_type`. |
| `session.error`                                                                  | A problem with the stream. See below.                                                                                                       |

The stream also sends frames without an `id`. These are not stored events and do not move your position:

* `: connected` is the first line. `: ping` follows every 15 seconds. They keep the connection alive; ignore them.
* When you connect to a running session, a `session.status_running` frame tells you the current state.
* When the session reaches a terminal status, the stream sends one last `session.status_idle` (completed) or `session.status_terminated` (failed or cancelled), with `stop_reason` and, on failure, `error`. Then it closes.

### Stream errors

| Frame                                                                 | Meaning                                                                                                          | What to do                                                                   |
| --------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- |
| `session.error` with an `id` and `error.code: event_record_too_large` | One event was too large to send. `error.content_path_template` gives the path to fetch it. The stream continues. | Fetch it with `getSessionEventContent`, or list events with `payloads=refs`. |
| `session.error` without an `id`, with `error.message`                 | The server could not read events. The stream closes.                                                             | Reconnect from your last event id.                                           |

The `Content-Type` of the response is `text/event-stream; charset=utf-8; max-record-bytes=<N>`, where `<N>` is the largest record the server may send. Size your SSE parser's buffer to that value. The TypeScript helper accepts records up to 8 MiB and throws on larger ones. If that happens, list events after your last id with `payloads=refs`, then resume the stream after the event you fetched.

## Recover from a dropped stream

Networks drop connections. The stream is designed so you can reconnect without losing events.

<Steps>
  <Step title="Track the last event you finished processing">
    Save a frame's `id` only after you have handled it. Frames without an `id` never change your position.
  </Step>

  <Step title="Reconnect from that id">
    Reconnect with `Last-Event-ID: <id>`, or with `after_event_id=<id>` in the URL. The server replays every event after it. If you use `after_event_id`, update it on every reconnect; reusing the original URL replays from the original position. A malformed id returns `400`. A well-formed id from another session does not fail; it is treated as a position.
  </Step>

  <Step title="Back off between attempts">
    Retry with exponential backoff and jitter. A `503` means the server is at its stream capacity; retry after a short wait.
  </Step>

  <Step title="Deduplicate by event id">
    Replays can repeat an event you already saw, so keep a set of handled `event_id` values and skip repeats.
  </Step>

  <Step title="Reconcile when the session ends">
    When several agents write at once, an event can be recorded slightly after a later-sorting event was already sent to you, and a cursor reader can miss it. After the stream closes at a terminal status, page through `listSessionEvents` from the start and add any event id you have not seen.
  </Step>
</Steps>

<Tip>
  If your process restarts and loses its place, list events with `listSessionEvents` to rebuild state, then open the stream with `after_event_id` set to the last id you listed.
</Tip>

## Watch many sessions with the gestalt

The gestalt is a live, compact summary of every root session in a time window: its status, execution state, and last activity. It is built for dashboards and alerting. Take one snapshot, draw it, then keep it current with the gestalt stream. Use it instead of polling `listSessions` or opening one event stream per session.

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={"theme":"css-variables"}
    const snapshot = await rl.managedAgents.listSessionGestalt({
      since: '2026-09-18T00:00:00Z',
      agent_id: 'b7e1c9a4-3d62-4f15-8a07-5c2e9f6d1b38',
    });
    ```
  </Tab>

  <Tab title="cURL">
    ```bash theme={"theme":"css-variables"}
    curl 'https://api.recursion.labelbox.com/managed-agents/v1/sessions/gestalt?since=2026-09-18T00:00:00Z&agent_id=b7e1c9a4-3d62-4f15-8a07-5c2e9f6d1b38' \
      -H "Authorization: Bearer $RECURSION_API_KEY"
    ```
  </Tab>
</Tabs>

```json theme={"theme":"css-variables"}
{
  "as_of": "2026-09-25T14:20:03.512Z",
  "sessions": [
    {
      "session_id": "e3a91f5c-7d24-4b68-9c10-2f8e6b4d7a53",
      "agent_id": "b7e1c9a4-3d62-4f15-8a07-5c2e9f6d1b38",
      "status": "active",
      "execution_state": "running",
      "created_at": "2026-09-25T14:02:11Z",
      "updated_at": "2026-09-25T14:19:48Z",
      "last_activity_at": "2026-09-25T14:19:48Z"
    }
  ]
}
```

`since` is required, in RFC 3339 format. It can be at most 30 days ago, or 400 days ago when you also pass `agent_id`. Sessions are ordered newest first by creation time. A row has `failed: true` when the session ended in a failure. `truncated: true` means only the newest 5,000 were returned; narrow the window or add `agent_id`. Both fields are left out when false.

### Keep the snapshot current

Open `streamSessionGestalt` with the same `since` and `agent_id`, and `after` set to the snapshot's `as_of`. It has no SDK method.

```bash theme={"theme":"css-variables"}
curl -N 'https://api.recursion.labelbox.com/managed-agents/v1/sessions/gestalt/stream?since=2026-09-18T00:00:00Z&agent_id=b7e1c9a4-3d62-4f15-8a07-5c2e9f6d1b38&after=2026-09-25T14:20:03.512Z' \
  -H "Authorization: Bearer $RECURSION_API_KEY"
```

| Frame                   | Meaning                                                                                | What to do                                                                     |
| ----------------------- | -------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ |
| `: connected`, `: ping` | Keep-alive. Nothing is sent while nothing changes.                                     | Ignore.                                                                        |
| `gestalt.change`        | `data` is `{as_of, sessions}` with the rows that changed. Its `id` is the new `as_of`. | Replace each row by `session_id`. Remove rows with `deleted: true`.            |
| `gestalt.reset`         | The server cannot continue from your position. The stream closes.                      | Take a new snapshot and reconnect from its `as_of`.                            |
| `gestalt.error`         | The server hit an error. The stream closes.                                            | Reconnect from the last `id` you received. If it repeats, take a new snapshot. |

To reconnect, send the last frame `id` as `after` or as the `Last-Event-ID` header. One of them is required. A stream position older than 30 days is rejected; take a new snapshot. The gestalt stream never closes on its own otherwise, so close it when your dashboard is done.

## What can go wrong

| Symptom or code                                              | Cause                                                                    | Fix                                                             |
| ------------------------------------------------------------ | ------------------------------------------------------------------------ | --------------------------------------------------------------- |
| `400 invalid_request` on `events`                            | Every event was dropped: unknown types or empty messages.                | Send `text` or `content` on each `user.message`.                |
| `events_accepted` is lower than the events you sent          | Some entries were dropped.                                               | Check each entry's `type` and content.                          |
| `404 not_found` when sending                                 | The session or a referenced session is missing or not visible to you.    | Check the ids and your access.                                  |
| `409 conflict` when sending                                  | The session is still being cancelled.                                    | Retry after a few seconds.                                      |
| The reply never arrives after `delivery_state: queued`       | The agent is still on its current turn.                                  | Wait, or interrupt the session.                                 |
| `409 conflict` when messaging a subagent                     | Subagent sessions in a terminal status do not resume.                    | Message the root session.                                       |
| `400 invalid_request` on `after_event_id`                    | The id is malformed or not lowercase.                                    | Pass an `event_id` exactly as returned.                         |
| The stream replays the same events after every reconnect     | The URL still has the original `after_event_id`.                         | Update or remove it on reconnect.                               |
| `503 service_unavailable` when opening a stream              | The server is at its stream capacity.                                    | Retry with backoff.                                             |
| `session.error` with `event_record_too_large`                | One event is larger than a stream record.                                | Fetch it with `getSessionEventContent`.                         |
| The TypeScript helper throws on a record over the size limit | The record is larger than 8 MiB.                                         | List events with `payloads=refs`, then resume after that event. |
| An event is missing from a long stream                       | A concurrent write was recorded out of order.                            | Re-read events after the session reaches a terminal status.     |
| The stream stays open after the agent stopped                | The session is interrupted or sleeping, not terminal.                    | Close the stream yourself, or cancel the session.               |
| `gestalt.reset`                                              | Your gestalt position can no longer be continued.                        | Take a new snapshot.                                            |
| `400 invalid_request` on `since`                             | The window is older than 30 days (400 with `agent_id`), or not RFC 3339. | Shorten the window or add `agent_id`.                           |

Every error uses the same body: `code`, `message`, and `details` with `field`, `requestId`, and `retryable`. See [Errors](/managed-agents/errors).

## Limits

* Referenced sessions: 10 per message.
* Event pages: about 8 MiB each.
* Stream keep-alive: a ping every 15 seconds.
* Gestalt: 5,000 sessions per snapshot, 30-day window (400 days with `agent_id`).

See [Limits](/managed-agents/limits) for the rest.

## Next steps

<CardGroup cols={2}>
  <Card title="Sessions" icon="play" href="/managed-agents/sessions">
    Start, interrupt, cancel, and delete sessions.
  </Card>

  <Card title="Session operations" icon="gauge" href="/managed-agents/session-operations">
    Pending inputs, compute usage, the analyst, and the session tree.
  </Card>

  <Card title="Multi-agent sessions" icon="sitemap" href="/managed-agents/multi-agent">
    Read events across threads and subagents.
  </Card>

  <Card title="Deliverables and artifacts" icon="box-archive" href="/managed-agents/artifacts">
    Ask for deliverables and see how they're kept.
  </Card>
</CardGroup>
