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

# Session operations

> Search sessions by metadata, see queued messages, measure compute, ask the session analyst, and read the board, tree, and threads.

These operations help you find, inspect, and understand sessions after you start them. Starting and stopping sessions is covered in [Sessions](/managed-agents/sessions), and the transcript in [Events](/managed-agents/events).

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

## Before you begin

* The organization user role can use every read on this page. Opening or questioning the session analyst needs the organization developer or admin role, because it starts a session. 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).

## Find sessions by metadata

Metadata is the set of key/value pairs you attach when you [start a session](/managed-agents/sessions), such as `customer_id` or `run`. Two calls list what is in use, so you can build filters without knowing the values in advance. Then filter `listSessions` with them.

<Tabs>
  <Tab title="Console">
    1. In the sidebar, click **Sessions**.
    2. Click the **Metadata** filter and choose a key from the suggestions.
    3. Type a value, or press Enter to match any value for that key. To combine filters, pick another key in the same field, which then reads **Add another**.
  </Tab>

  <Tab title="TypeScript">
    ```typescript theme={"theme":"css-variables"}
    const { keys } = await rl.managedAgents.listSessionMetadataKeys();

    const { values, truncated } = await rl.managedAgents.listSessionMetadataValues({
      key: 'customer_id',
      q: 'acme-',
      limit: 50,
    });

    const sessions = await rl.managedAgents.listSessions({
      metadata: ['customer_id:acme-042'],
      metadata_key: ['run'],
      root_only: true,
    });
    ```
  </Tab>

  <Tab title="cURL">
    ```bash theme={"theme":"css-variables"}
    curl 'https://api.recursion.labelbox.com/managed-agents/v1/sessions/metadata-keys' \
      -H "Authorization: Bearer $RECURSION_API_KEY"

    curl 'https://api.recursion.labelbox.com/managed-agents/v1/sessions/metadata-values?key=customer_id&q=acme-&limit=50' \
      -H "Authorization: Bearer $RECURSION_API_KEY"

    curl 'https://api.recursion.labelbox.com/managed-agents/v1/sessions?metadata=customer_id:acme-042&metadata_key=run&root_only=true' \
      -H "Authorization: Bearer $RECURSION_API_KEY"
    ```
  </Tab>
</Tabs>

```json theme={"theme":"css-variables"}
{ "values": ["acme-017", "acme-042"], "truncated": false }
```

* `listSessionMetadataKeys` returns `{ keys, truncated }`: every key in use in your organization, sorted, up to 200.
* `listSessionMetadataValues` returns `{ values, truncated }` for one `key`. `q` is a case-sensitive prefix of up to 512 characters. `limit` defaults to 50 and can be at most 200. An unknown key returns an empty list, not an error.
* When `truncated` is `true`, there are more matches than returned. Narrow `q`; there is no page token.

In `listSessions`, each `metadata=key:value` must match, and each `metadata_key` must be present. Subagent sessions inherit their root's match, so add `root_only=true` to get one row per run. See [List sessions](/managed-agents/sessions) for the other filters.

## See queued messages

A message sent while the agent is mid-turn is queued until the turn ends. `listSessionPendingInputs` shows those accepted messages that are not in the transcript yet, in the order they were accepted. Nothing needs answering: these are messages you or a teammate already sent. To have the agent read them now instead of at the end of the turn, interrupt the session.

<Tabs>
  <Tab title="Console">
    Open the session. Queued messages appear under the last event, marked **Queued**. Click **Interrupt to read now** to have the agent read them immediately.
  </Tab>

  <Tab title="TypeScript">
    ```typescript theme={"theme":"css-variables"}
    const { pending_inputs } = await rl.managedAgents.listSessionPendingInputs({
      session_id: 'e3a91f5c-7d24-4b68-9c10-2f8e6b4d7a53',
    });
    if (pending_inputs.length > 0) {
      await rl.managedAgents.interruptSession({
        session_id: 'e3a91f5c-7d24-4b68-9c10-2f8e6b4d7a53',
        body: {},
      });
    }
    ```
  </Tab>

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

```json theme={"theme":"css-variables"}
{
  "pending_inputs": [
    {
      "input_id": "01997a41-2c9e-7f03-b8d4-5a1e6c3f9d27",
      "kind": "message",
      "event": {
        "event_type": "message",
        "actor": "human:api",
        "role": "user",
        "content": { "blocks": [{ "type": "text", "text": "Also check last week's incidents." }] }
      },
      "created_at": "2026-09-25T14:11:30Z"
    }
  ]
}
```

`kind` is `message` or `interrupt`. When the agent reads a queued message, it moves into the transcript with its `input_id` as the `event_id`, so you can match the two.

## Check compute usage

`listSessionResourceSamples` returns CPU, memory, disk, and GPU usage of the sandboxes in a session tree, in one-minute buckets. Use it to right-size an environment or to see why a run was slow. Any session id in the tree works.

<Tabs>
  <Tab title="Console">
    Open the session and click the **Compute** tab.
  </Tab>

  <Tab title="TypeScript">
    ```typescript theme={"theme":"css-variables"}
    const usage = await rl.managedAgents.listSessionResourceSamples({
      session_id: 'e3a91f5c-7d24-4b68-9c10-2f8e6b4d7a53',
      from: '2026-09-25T14:00:00Z',
      to: '2026-09-25T15:00:00Z',
      resolution: 'minute',
      limit: 120,
    });
    ```
  </Tab>

  <Tab title="cURL">
    ```bash theme={"theme":"css-variables"}
    curl 'https://api.recursion.labelbox.com/managed-agents/v1/sessions/e3a91f5c-7d24-4b68-9c10-2f8e6b4d7a53/resource-samples?from=2026-09-25T14:00:00Z&to=2026-09-25T15:00:00Z&resolution=minute&limit=120' \
      -H "Authorization: Bearer $RECURSION_API_KEY"
    ```
  </Tab>
</Tabs>

```json theme={"theme":"css-variables"}
{
  "session_id": "e3a91f5c-7d24-4b68-9c10-2f8e6b4d7a53",
  "root_session_id": "e3a91f5c-7d24-4b68-9c10-2f8e6b4d7a53",
  "resolution": "minute",
  "live": true,
  "sandboxes": [
    {
      "sandbox_id": "7a3c9e15-2b84-4d60-9f71-0e6d2c8b5a43",
      "attached_at": "2026-09-25T14:02:40Z",
      "sample_interval_ms": 5000
    }
  ],
  "buckets": [
    {
      "sandbox_id": "7a3c9e15-2b84-4d60-9f71-0e6d2c8b5a43",
      "bucket_start": "2026-09-25T14:05:00Z",
      "complete": true,
      "sample_count": 12,
      "cpu_millicores_avg": 840,
      "cpu_millicores_peak": 1910,
      "cpu_capacity_millicores": 2000,
      "memory_working_set_avg_bytes": 1610612736,
      "memory_working_set_peak_bytes": 2147483648,
      "memory_capacity_bytes": 4294967296,
      "disk_used_avg_bytes": 3221225472,
      "disk_capacity_bytes": 21474836480
    }
  ],
  "as_of": "2026-09-25T14:06:12Z"
}
```

| Parameter    | Description                                                                                                                                                                                                   |
| ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `from`       | Earliest bucket start, inclusive. RFC 3339. Omit to start from the beginning of the tree.                                                                                                                     |
| `to`         | Latest bucket start, exclusive. RFC 3339. Omit for now.                                                                                                                                                       |
| `resolution` | `minute` (default) returns per-minute averages and peaks. `raw` also returns each sample, about every 5 seconds, in `offsets_ms` and parallel arrays such as `cpu_millicores` and `memory_working_set_bytes`. |
| `limit`      | Buckets per page, 1 to 720. Default 120.                                                                                                                                                                      |
| `page_token` | The `next_page_token` from the previous page. It is present only when more buckets remain.                                                                                                                    |

| Metric | Fields                                                                                                                                                                       |
| ------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| CPU    | `cpu_millicores_avg`, `cpu_millicores_peak`, `cpu_capacity_millicores`, `cpu_limit_millicores`. 1000 millicores is one fully busy core. Divide by capacity for a percentage. |
| Memory | `memory_working_set_avg_bytes`, `memory_working_set_peak_bytes`, `memory_capacity_bytes`, `memory_limit_bytes`.                                                              |
| Disk   | `disk_used_avg_bytes`, `disk_used_peak_bytes`, `disk_capacity_bytes` for the workspace volume.                                                                               |
| GPU    | `gpu_count`, `gpu_utilization_avg`, `gpu_utilization_peak`, `gpu_memory_used_peak_bytes`, `gpu_memory_total_bytes`. Absent without GPUs.                                     |

Buckets are ordered by sandbox, then time. Each names its `sandbox_id`; `sandboxes` lists every sandbox the tree has had, oldest first, with `attached_at`, `detached_at`, and `detach_reason` (`released`, `closed`, `replaced`, or `lost`). A bucket with `complete: false` is still filling and changes on the next read. Keep polling while `live` is `true`. An empty `sandboxes` list means the tree never had a sandbox or sampling is off.

## Ask the session analyst

The session analyst is an assistant that answers questions about a session tree: what the agents were asked to do, what they did, which tools failed, how they coordinated, what it cost, and how it was graded. It reads the whole tree. It is read-only: it has no sandbox and no credentials, and nothing it does changes the session.

<Tabs>
  <Tab title="Console">
    1. Open the session and click **Ask** in the header.
    2. Type a question in **Ask about this session…**, or pick a suggested one.
  </Tab>

  <Tab title="TypeScript">
    ```typescript theme={"theme":"css-variables"}
    const opened = await rl.managedAgents.openSessionAnalyst({
      session_id: 'e3a91f5c-7d24-4b68-9c10-2f8e6b4d7a53',
      body: { question: 'Why did this session end the way it did?' },
    });
    const analystId = opened.session!.session_id;

    await rl.managedAgents.sendSessionEvents({
      session_id: analystId,
      body: { message: 'Which tool calls failed, and what did the agent do about them?' },
    });
    ```
  </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/analyst' \
      -H "Authorization: Bearer $RECURSION_API_KEY" \
      -H 'Content-Type: application/json' \
      -d '{"question": "Why did this session end the way it did?"}'
    ```
  </Tab>
</Tabs>

```json theme={"theme":"css-variables"}
{
  "created": true,
  "session": {
    "session_id": "c4f82d19-6a37-4e5b-8d01-9b2e7f3a6c58",
    "kind": "session_analyst",
    "status": "active",
    "execution_state": "running",
    "created_at": "2026-09-25T14:21:07Z"
  }
}
```

| Request                         | Result                                                                                                                            |
| ------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
| Empty body                      | Returns your existing analyst conversation for this tree, or `created: false` with no `session` if you have none. Starts nothing. |
| `question`, no conversation yet | Starts the analyst with your question as its first turn. Returns `201` and `created: true`.                                       |
| `question`, conversation exists | Delivers the question to it. Returns `200` and `created: false`.                                                                  |
| `reset: true`                   | Ends your current conversation. It stays readable. Your next question starts a new one.                                           |

Read the analyst's answers from its session's [events or event stream](/managed-agents/events), using the returned `session_id`. Ask follow-ups with `sendSessionEvents` on that id. Each person has one analyst conversation per tree, and any session id in the tree opens the same one. Analyst sessions are left out of `listSessions` unless you filter with `kind=session_analyst`.

<Note>
  The analyst runs on a fixed model, and its model usage is recorded for your organization like any other session's. Each question adds usage, so reuse a conversation rather than resetting it for every question. See [Usage and cost](/managed-agents/usage-and-cost).
</Note>

## Read the team board

When a session works as a team, agents coordinate through a shared board of tasks and posts. `getSessionBoard` returns its current state: `tasks`, `posts`, `members`, and `as_of`. Each task has a `seq` shown as `T1`, `T2`, and so on, a `kind` (`task`, `explore`, or `review`), a `status` (`open`, `claimed`, `blocked`, `done`, or `dropped`), and an `owner_session_id` once claimed.

<Tabs>
  <Tab title="Console">
    Open the session and click the **Work** tab.
  </Tab>

  <Tab title="TypeScript">
    ```typescript theme={"theme":"css-variables"}
    const board = await rl.managedAgents.getSessionBoard({
      session_id: 'e3a91f5c-7d24-4b68-9c10-2f8e6b4d7a53',
    });
    ```
  </Tab>

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

The board is the source of truth for team state. `board_update` events can be missed by a reader following events by cursor, so read the board when you need the current picture. A session started with team mode `off` has no board and returns `404`. Under `auto` or `on`, a board with nothing posted yet comes back empty. See [Teams](/managed-agents/teams).

## Read the session tree and threads

A session that delegates work becomes a tree: the root session and the subagent sessions it starts. Each agent's work runs in a thread. See [Multi-agent sessions](/managed-agents/multi-agent) for how trees form.

`getSessionTree` returns the whole tree in one call: `root_session_id`, every session in `sessions`, every thread in `threads`, and the first page of the combined timeline in `events`. It accepts `hydrate`, `image_urls`, and `payloads`, like [List events](/managed-agents/events). Any session id in the tree works.

<Tabs>
  <Tab title="Console">
    Open the session and click the **Threads** tab. The tab appears when the session has more than one thread.
  </Tab>

  <Tab title="TypeScript">
    ```typescript theme={"theme":"css-variables"}
    const tree = await rl.managedAgents.getSessionTree({
      session_id: 'e3a91f5c-7d24-4b68-9c10-2f8e6b4d7a53',
    });

    const more = tree.next_event_id
      ? await rl.managedAgents.listSessionEvents({
          session_id: tree.root_session_id,
          after_event_id: tree.next_event_id,
        })
      : undefined;

    const { threads } = await rl.managedAgents.listSessionThreads({
      session_id: 'e3a91f5c-7d24-4b68-9c10-2f8e6b4d7a53',
    });

    const thread = await rl.managedAgents.getSessionThread({
      session_id: 'e3a91f5c-7d24-4b68-9c10-2f8e6b4d7a53',
      thread_id: 'f1d6a82c-4e97-4b35-a0c8-7e2b9d5f3a16',
    });
    ```
  </Tab>

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

    curl 'https://api.recursion.labelbox.com/managed-agents/v1/sessions/e3a91f5c-7d24-4b68-9c10-2f8e6b4d7a53/threads' \
      -H "Authorization: Bearer $RECURSION_API_KEY"
    ```
  </Tab>
</Tabs>

```json theme={"theme":"css-variables"}
{
  "threads": [
    {
      "thread_id": "f1d6a82c-4e97-4b35-a0c8-7e2b9d5f3a16",
      "session_id": "f1d6a82c-4e97-4b35-a0c8-7e2b9d5f3a16",
      "root_session_id": "e3a91f5c-7d24-4b68-9c10-2f8e6b4d7a53",
      "parent_session_id": "e3a91f5c-7d24-4b68-9c10-2f8e6b4d7a53",
      "agent_id": "b7e1c9a4-3d62-4f15-8a07-5c2e9f6d1b38",
      "name": "researcher",
      "role": "subagent",
      "status": "idle",
      "stop_reason": { "type": "end_turn" },
      "thread_path": "/f1d6a82c-4e97-4b35-a0c8-7e2b9d5f3a16",
      "created_at": "2026-09-25T14:06:55Z",
      "updated_at": "2026-09-25T14:14:20Z"
    }
  ]
}
```

* The tree's `events` is only the first page. When `next_event_id` is set, continue with `listSessionEvents` and `after_event_id` set to it.
* `listSessionThreads` returns every thread, sorted by `thread_path`, including one for each session in the tree. A subagent's `thread_id` is its session id. `thread_path` is the chain of session ids below the root, such as `/<child>/<grandchild>`; the root's is `/`.
* Thread `role` is `primary` (the root), `subagent`, or `grader`. Thread `status` is `running`, `idle`, or `terminated`.
* `getSessionThread` returns `404` for a thread outside the tree.

## Attach and detach files

`listSessionResources` lists the files attached to a session, each with its `mount_path` in the sandbox once it's in place. `addSessionResources` attaches more while the session runs, `getSessionResource` reads one attachment, and `deleteSessionResource` detaches one. Attach to the root session of a multi-agent tree; everyone in the tree shares its sandbox. See [Attach and detach files while a session runs](/managed-agents/files#attach-and-detach-files-while-a-session-runs).

<Tabs>
  <Tab title="Console">
    Open the session and choose the **Files** tab in the side panel. Click **Attach files** to add files, or detach one from its row.
  </Tab>

  <Tab title="TypeScript">
    ```typescript theme={"theme":"css-variables"}
    const { resources } = await rl.managedAgents.listSessionResources({
      session_id: 'e3a91f5c-7d24-4b68-9c10-2f8e6b4d7a53',
    });
    ```
  </Tab>

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

```json theme={"theme":"css-variables"}
{
  "resources": [
    {
      "type": "file",
      "resource_id": "4a7c2e91-6b35-4d08-9f1e-3c8a5b7d2e64",
      "file_id": "9f8b1c2d-3e4f-5a6b-7c8d-9e0f1a2b3c4d",
      "filename": "signups.csv",
      "media_type": "text/csv",
      "byte_size": 48213,
      "sha256": "7d3b6e2a8c5f0d1b9e7a3c6f2d8b4e0a1c7f5d934f1c9a0e7b2d5836c1e04a9f",
      "relative_path": "data/signups.csv",
      "mount_path": "/workspace/.managed-agents/files/data/signups.csv",
      "created_at": "2026-09-25T10:21:40Z"
    }
  ]
}
```

## What can go wrong

| Symptom or code                                         | Cause                                                                                                  | Fix                                                                                                                             |
| ------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------- |
| `400 invalid_request` on `key`                          | The key has characters other than letters, digits, `_`, `.`, and `-`, or is longer than 64 characters. | Use the key exactly as returned by `listSessionMetadataKeys`.                                                                   |
| `400 invalid_request` on `limit`                        | `limit` is out of range: above 200 for metadata values, or outside 1 to 720 for resource samples.      | Use a value in range.                                                                                                           |
| `truncated: true` on metadata values                    | More values match than one response holds.                                                             | Narrow `q`.                                                                                                                     |
| A metadata filter returns subagent sessions             | Subagents match their root's metadata.                                                                 | Add `root_only=true`.                                                                                                           |
| `pending_inputs` is empty but the agent has not replied | The message was already read, or the session is not running.                                           | Read the transcript; send a message to resume an idle session.                                                                  |
| `sandboxes` is empty                                    | The tree never had a sandbox, or sampling is off.                                                      | Nothing to fix.                                                                                                                 |
| Latest resource bucket keeps changing                   | It has `complete: false`.                                                                              | Treat only complete buckets as final.                                                                                           |
| `403 forbidden` opening the analyst                     | Your role can read but not start sessions.                                                             | See [Organizations and roles](/managed-agents/organizations-and-roles). Use an account or key with the developer or admin role. |
| `503` `session_analyst_busy`                            | Another open of the same conversation is in progress.                                                  | Retry after a moment.                                                                                                           |
| `503` `session_analyst_unconfigured`                    | The analyst couldn't be started for your organization.                                                 | Contact support.                                                                                                                |
| `409 conflict` attaching a file                         | The path collides with another attachment, or the session isn't the root of its tree.                  | Choose another `relative_path`, or attach to the root session.                                                                  |
| `503` `session_analyst_model_unavailable`               | The analyst's model is not available right now, so a new conversation cannot start.                    | Retry later.                                                                                                                    |
| `404 not_found` reading the board                       | The session runs with team mode `off`.                                                                 | Use the tree and events instead.                                                                                                |
| Board state differs from the events you followed        | A `board_update` event was missed.                                                                     | Read the board again.                                                                                                           |
| `404 not_found` on a thread                             | The thread is not in this session's tree.                                                              | List threads first.                                                                                                             |

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

## Limits

* Metadata keys: up to 200 per response. Metadata values: up to 200 per response.
* Resource samples: up to 720 buckets per page.
* Session analyst: one conversation per person per session tree.

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

## Next steps

<CardGroup cols={2}>
  <Card title="Sessions" icon="play" href="/managed-agents/sessions">
    Start sessions, check success, and stop them.
  </Card>

  <Card title="Events" icon="wave-pulse" href="/managed-agents/events">
    Read and stream the transcript.
  </Card>

  <Card title="Teams" icon="users" href="/managed-agents/teams">
    Let copies of an agent share work through the board.
  </Card>

  <Card title="Usage and cost" icon="chart-line" href="/managed-agents/usage-and-cost">
    See what each session cost.
  </Card>
</CardGroup>
