> ## 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](/recursion/sessions), and the transcript in [Events](/recursion/events).

<Note>
  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](/recursion/organizations-and-roles). For API calls, create a key under **API keys**; see [API keys](/recursion/api-keys).
</Note>

## Find sessions by metadata

Metadata is the set of key/value pairs you attach when you [start a session](/recursion/sessions), such as `customer_id` or `run`. List the keys and values 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="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 }
```

When `truncated` is `true`, there are more matches than returned. Narrow `q`; there is no page token. Subagent sessions inherit their root's match, so add `root_only=true` to get one row per run.

<Accordion title="Metadata keys, values, and filters">
  * `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.
  * In `listSessions`, each `metadata=key:value` must match, and each `metadata_key` must be present. See [List sessions](/recursion/sessions) for the other filters.
</Accordion>

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

<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="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. To have the agent read them now instead of at the end of the turn, interrupt the session.

## 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="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"
}
```

Keep polling while `live` is `true`. A bucket with `complete: false` is still filling and changes on the next read.

<Accordion title="Parameters and metrics">
  | 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. |
</Accordion>

<Accordion title="Buckets and sandboxes">
  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`). An empty `sandboxes` list means the tree never had a sandbox or sampling is off.
</Accordion>

## 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, and what it cost. 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="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"
  }
}
```

Read the analyst's answers from its session's [events or event stream](/recursion/events), using the returned `session_id`. Ask follow-ups with `sendSessionEvents` on that id.

<Accordion title="What each analyst request does">
  | 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. |

  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`.
</Accordion>

<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](/recursion/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`.

<Tabs>
  <Tab title="Console">
    Open the session and click the **Work** tab.
  </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. See [Teams](/recursion/teams).

<Accordion title="Board tasks and team modes">
  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.

  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.
</Accordion>

## 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. `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`. Any session id in the tree works. See [Multi-agent sessions](/recursion/multi-agent) for how trees form.

<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="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.

<Accordion title="Tree and thread details">
  * `getSessionTree` accepts `hydrate`, `image_urls`, and `payloads`, like [List events](/recursion/events).
  * `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` for the root and `subagent` for a delegated session. Thread `status` is `running`, `idle`, or `terminated`.
  * `getSessionThread` returns `404` for a thread outside the tree.
</Accordion>

## Attach and detach files

Open the session and choose the **Files** tab in the side panel. Under **Inputs**, click **Attach files…** to add files, or click the **×** on a row to detach it. 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](/recursion/files#attach-and-detach-files-while-a-session-runs).

## What can go wrong

The most common problems:

* **A metadata filter returns subagent sessions.** Subagents match their root's metadata. Add `root_only=true`.
* **`403 forbidden` opening the analyst.** Your role can read but not start sessions. Use an account or key with the developer or admin role. See [Organizations and roles](/recursion/organizations-and-roles).
* **`404 not_found` reading the board.** The session runs with team mode `off`. Use the tree and events instead.

<Accordion title="Every session operations error">
  | 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](/recursion/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. |
  | A file can't be attached mid-session | The path collides with another attachment, or the session isn't the root of its tree. | Choose another 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 is a flat object with required `code` and `message` fields and optional `details`, which can include `field`, `requestId`, and `retryable`. See [Errors](/recursion/errors).
</Accordion>

## Limits

* Metadata keys: up to 200 per response. Metadata values: up to 200 per response.
* Session analyst: one conversation per person per session tree.

Resource-sample pages and every other limit are in [Limits](/recursion/limits).

## Next steps

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

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

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

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