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

# Sessions

> Start a session, watch it work, and continue, stop, or clean it up.

A session is one run of an agent on a task. It runs in its own sandbox, pins the agent version and environment it started with, and keeps a transcript you can stream, read, and continue.

<Note>
  You need an [agent](/recursion/agents) and an [environment](/recursion/environments). If the environment has a setup script, it must have passed a current setup run. Starting, messaging, and stopping sessions needs the Developer or Admin role; the User role can list and read them. API calls use a key from [API keys](/recursion/api-keys).
</Note>

## Start a session

Send the agent, the environment, and the task. Name the files you want back, so the agent saves them as deliverables.

<Tabs>
  <Tab title="Console">
    1. In the sidebar, click **Sessions**, then click **Launch session**.
    2. Under **Configuration**, select an **Agent** and an **Environment**.
    3. Under **Task**, write the **Opening message**: the task, the deliverables you want by name, and what done looks like.
    4. Optionally, choose vaults under **Credentials**, a mode under **Team**, and add **Custom metadata**.
    5. Click **Launch session**.
  </Tab>

  <Tab title="cURL">
    ```bash theme={"theme":"css-variables"}
    curl -X POST 'https://api.recursion.labelbox.com/managed-agents/v1/sessions' \
      -H "Authorization: Bearer $RECURSION_API_KEY" \
      -H 'Content-Type: application/json' \
      -H 'Idempotency-Key: weekly-risk-review-2026-09-25' \
      -d '{
        "agent_id": "b7e1c9a4-3d62-4f15-8a07-5c2e9f6d1b38",
        "environment_id": "4c8e2a61-9b3f-4d70-a5e2-1f6b8c9d0e47",
        "message": "Review the open incidents and write a risk summary named risks.md.",
        "metadata": {"customer_id": "acme-042", "run": "weekly"}
      }'
    ```
  </Tab>
</Tabs>

The session is accepted right away and starts in the background. The response is `202 Accepted`:

```json theme={"theme":"css-variables"}
{
  "session_id": "e3a91f5c-7d24-4b68-9c10-2f8e6b4d7a53",
  "status_path": "/managed-agents/v1/sessions/e3a91f5c-7d24-4b68-9c10-2f8e6b4d7a53"
}
```

Keep `session_id`. `202` means the session was accepted, not that it is running. `status_path` is relative to `https://api.recursion.labelbox.com` and already starts with `/managed-agents`, as does the `Location` response header.

The `Idempotency-Key` header is required. Use a value that identifies the work, such as a job id, and send it again on every retry: the same request with the same key returns the same session instead of starting a second one.

<Accordion title="All start fields">
  | Field | Required | Description |
  | - | - | - |
  | `agent_id` | Yes | The agent to run. The session pins the agent's current version at start and reports it as `agent_version_id`. You cannot choose an older version here. |
  | `environment_id` | Yes | The environment the sandbox is built from. If it has a setup script without a current passing setup run, the request fails with `422 environment_not_verified`. |
  | `message` | No | The opening message: the task and what done looks like. It stays in the agent's context for the whole run, so context compaction cannot summarize it away. A message of only whitespace returns `400`. |
  | `resources` | No | Up to 500 files to mount read-only in the sandbox before the first turn, as `[{ "type": "file", "file_id" }]`. One unknown file id refuses the whole request with `404`. See [Files](/recursion/files). |
  | `vault_ids` | No | Vaults to grant. Omit to use the agent's default vaults. `[]` grants none. See [Vaults](/recursion/vaults). |
  | `credential_refs` | No | `[{ "vault_id", "credential_id" }]`. Narrows access to exactly these credentials across the granted vaults. `[]` with non-empty `vault_ids` is rejected. |
  | `referenced_session_ids` | No | Up to 10 earlier sessions the agent may read. If you cannot read one of them, the request fails with `404` and nothing starts. See [Referenced sessions](/recursion/referenced-sessions). |
  | `metadata` | No | Your own string key/value pairs, such as a customer id. Up to 32 entries. Keys are up to 64 characters of letters, digits, `_`, `.`, and `-`. Values are 1 to 512 characters without control characters. Set only at start: it's returned on the root session by `getSession` and `listSessions`, you can filter on it, and there is no call to change it. See [Session operations](/recursion/session-operations) for metadata search. |
  | `team` | No | `{ "mode": "auto" \| "on" \| "off" }`. Omit to keep the agent's setting, which is `auto` unless the agent sets another. See [Teams](/recursion/teams). |
</Accordion>

<Accordion title="Retry a start safely">
  | You send | Result |
  | - | - |
  | No key | `400 invalid_request`. |
  | A key that is empty, longer than 256 characters, has non-visible characters, or is sent more than once | `400 invalid_request`. |
  | The same key, exact HTTP method, escaped path, raw query, and raw body bytes | The original `202` response again, with `Idempotency-Replayed: true`. No second session starts. The first response carries `Idempotency-Replayed: false`. |
  | The same key with any different request-identity bytes | `409 idempotency_conflict`. |
  | The same key while the first request is still being processed | `409 idempotency_in_progress` with `Retry-After`. Wait, then resend the same request byte for byte. |
  | The same key after the first attempt failed with a non-2xx response | The key is released. The retry runs as a new attempt. |
  | The key cannot be checked or reserved | `503 idempotency_unavailable`. The request did not run, so resend the same request with the same key. |

  Keys share one namespace across keyed mutations in your organization, so include the operation and work identity. Completed receipts last about 24 hours, pending claims can be reclaimed after about 1 hour, and deduplication is not guaranteed after expiry. The session id is derived deterministically from the durable receipt, so an exact replay returns the same `session_id`. See [Idempotent mutations](/recursion/api#idempotent-mutations).
</Accordion>

## Watch it work

Stream the session's events to follow along as the agent works. The stream replays what already happened, then stays open for new events, and closes when the session reaches a terminal status.

<Tabs>
  <Tab title="Console">
    Open the session from **Sessions**. The transcript updates live, and the status badge shows the current state.
  </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>

You see the agent's steps as they happen. This output is abridged; each `data` line carries the full event.

```text theme={"theme":"css-variables"}
event: session.status_running
event: agent.tool_use
event: agent.tool_result
event: agent.message
data: {"type":"agent.message","content":[{"type":"text","text":"Found 12 open incidents."}]}
event: agent.artifact
event: session.status_idle
data: {"type":"session.status_idle","stop_reason":"end_turn"}
```

[Events](/recursion/events) lists every frame type and shows how to resume a dropped stream. To poll instead, read the session with `view=summary` and stop on any terminal status, not only `completed`, and also when the session needs you.

<Warning>
  An interrupted or sleeping session stays `active`. A loop that waits only for a terminal status keeps waiting until someone sends a message, the wake time arrives, or the session is cancelled.
</Warning>

## What success means

When the agent loop finishes, `status` becomes `completed` whether or not the task succeeded. Read the final message and open the files it saved to judge the work.

| You see | Result |
| - | - |
| `status: completed`, `execution_state: idle`, `stop_reason: end_turn` | The agent finished. Check its final message and deliverables against your task. |
| `status: failed` | The session hit an error. Read `failure`. |
| `status: cancelled` | The session was cancelled. Events and deliverables stay readable. |
| `status: active`, `execution_state: idle`, `stop_reason: interrupted` or `sleeping` | Not finished. The session is paused and waits for a message or its wake time. |
| `status: active`, `execution_state: idle`, `stop_reason: requires_action` | Not finished. The session needs your review before it continues. |
| `status: active`, `execution_state: idle`, `stop_reason: out_of_credit` | Not finished. Your tenant ran out of credit. Buy credits; it continues on its own within about a minute if it paused in the last 72 hours. |

When a session fails, `failure` holds `phase`, `code`, `message`, `retryable`, `category`, and `at`. `category` is `transient` (retry), `caller_error` (fix the request or setup), or `internal`.

Deliverables appear in the session's **Files** tab. Files the agent leaves elsewhere stay in `/workspace` for later turns, even after a stopped sandbox is released, and can be used when the workspace reopens successfully, but they aren't captured as deliverables. Deleting the session removes access to its workspace. See [Deliverables and artifacts](/recursion/artifacts) and [Download session deliverables](/recursion/files#download-session-deliverables). To give a session input files, attach them in **Launch session**, with `resources` at start, or in the session's **Files** tab while it runs.

<Accordion title="Read a session's details">
  In **Sessions**, click a session. The **Session** tab shows its state, agent, environment, and **Custom metadata**. A session an automation started also shows **Started by**: the **Automation**, the **Trigger** that fired, and the **Run**, each linking back to the automation. See [Automations](/recursion/automations).

  With the API, `view=full` (the default) returns the whole session, including the agent and model snapshots and the resolved configuration. `view=summary` omits `agent_snapshot`, `model_snapshot`, and `config`, which makes polling cheaper. Any other `view` value returns `400`.

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

  ```json theme={"theme":"css-variables"}
  {
    "session_id": "e3a91f5c-7d24-4b68-9c10-2f8e6b4d7a53",
    "root_session_id": "e3a91f5c-7d24-4b68-9c10-2f8e6b4d7a53",
    "session_path": "/",
    "kind": "chat",
    "status": "completed",
    "execution_state": "idle",
    "stop_reason": "end_turn",
    "agent_id": "b7e1c9a4-3d62-4f15-8a07-5c2e9f6d1b38",
    "agent_version_id": "9d2f7b1e-6c43-4a85-b0e9-3a7c5d8f2e16",
    "environment_id": "4c8e2a61-9b3f-4d70-a5e2-1f6b8c9d0e47",
    "metadata": { "customer_id": "acme-042", "run": "weekly" },
    "costUsd": "0.418204",
    "costState": "final",
    "created_at": "2026-09-25T14:02:11Z",
    "updated_at": "2026-09-25T14:19:48Z",
    "last_activity_at": "2026-09-25T14:19:48Z"
  }
  ```

  | Field | Description |
  | - | - |
  | `root_session_id`, `parent_session_id`, `session_path` | Where the session sits in its tree. A root has `session_path` `/` and no parent. |
  | `kind` | `chat` for sessions you start, `subagent` for sessions an agent starts, `session_analyst` for the [session analyst](/recursion/session-operations). |
  | `agent_version_id` | The agent version pinned at start. |
  | `metadata` | Set at start. Present on root sessions only. |
  | `wake_at` | When a sleeping session wakes on its own. |
  | `concurrency_slot_held` | Whether the session counts against the agent's `max_concurrent_sessions`. |
  | `costUsd`, `costState` | What the whole session tree cost, and whether that is `final` or `so_far`. See [Usage and cost](/recursion/usage-and-cost). |
  | `failure` | Present only after a failure. |
</Accordion>

## Continue the conversation

Send a follow-up to add to the task or ask for changes. A running session reads it at its next turn. An idle or finished root session picks the work up again. The full send options, including the delivery states in the response, are in [Events](/recursion/events).

<Tabs>
  <Tab title="Console">
    Open the session, type in **Message the agent…**, and click **Send**. While the agent is working the button reads **Queue**.
  </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 '{"message": "Also add a mitigation for each high-severity item."}'
    ```
  </Tab>
</Tabs>

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

Only root sessions resume. A message to a subagent session in a terminal status returns `409 conflict`; message the root session instead.

To change direction mid-turn, interrupt instead:

* **Interrupt** stops the current model call and gives a running tool a short grace period to stop. The session waits, with its sandbox, for your next message.
* **Interrupt and send** stops the current turn and starts a new one with your message, in one call.

<Accordion title="Interrupt, and interrupt and send">
  An interrupt stops the current model call right away and gives a running tool a short grace period to stop. If messages are queued, the newest starts a fresh turn. Otherwise the session stays `active` and goes `idle` with `stop_reason: interrupted`, ready for your next message. The sandbox stays up. In the console, click **Interrupt** under the last event while the agent is working.

  ```bash theme={"theme":"css-variables"}
  curl -X POST 'https://api.recursion.labelbox.com/managed-agents/v1/sessions/e3a91f5c-7d24-4b68-9c10-2f8e6b4d7a53/interrupt' \
    -H "Authorization: Bearer $RECURSION_API_KEY" \
    -H 'Content-Type: application/json' \
    -d '{}'
  ```

  The response is `{ "interrupted": true }`. A session with nothing running (`idle` and not sleeping or waiting on its subagents, or already finished) answers `409 conflict`, and nothing is recorded. `interrupted: false` means the stop was recorded but reaching the running work was not confirmed, usually because the run ended as the request arrived.

  `interruptAndSendSessionMessage` stops the current turn and starts a fresh one with your message in one call. Send `message` for plain text, or `content` for content blocks; `content` wins if you send both. In the console, type your message and click **Interrupt & send**, which appears while the agent is working.

  ```bash theme={"theme":"css-variables"}
  curl -X POST 'https://api.recursion.labelbox.com/managed-agents/v1/sessions/e3a91f5c-7d24-4b68-9c10-2f8e6b4d7a53/interrupt-and-send' \
    -H "Authorization: Bearer $RECURSION_API_KEY" \
    -H 'Content-Type: application/json' \
    -d '{"message": "Stop. Only cover incidents opened this week."}'
  ```

  The response has the same shape as a send, with `events_accepted: 2`: the interrupt and your message.
</Accordion>

### Send an image

To show the agent a screenshot or chart, attach it to a message. The model sees the image when its model accepts images. In the session's message box, click **Attach image**, choose the image, write your message, and click **Send**. To use an image from your library, click **Attach from Files**. Send PNG, JPEG, or WebP; the type is detected from the bytes. An image the session's image limits refuse returns `400` with the reason. See [Limits](/recursion/limits).

## Stop or delete a session

| Action | What happens | Console |
| - | - | - |
| Cancel | Stops the session now. In-flight work is abandoned, the sandbox is torn down, and the status becomes `cancelled` with `stop_reason: interrupted`. Events and deliverables stay readable, and a follow-up message to a cancelled root session starts it again in a new sandbox. | **Session actions** › **Force stop…** › **Stop now** |
| Delete | Removes the session and all its subagent sessions from lists and reads, stops any running work, and tears down the sandbox. The transcript is retained, not erased. There is no undo. | **Session actions** › **Delete session…** › **Delete** |

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

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

Cancel answers `{ "cancelled": true }` and delete `{ "deleted": true }`. You can cancel any session that is not completed, failed, or already cancelled, including an idle one. The cancel response is the same when the session was already finished, so it is safe to repeat. Sandbox teardown can finish shortly after the response.

## Find sessions

`listSessions` returns sessions in your organization, most recently updated first. Filter by status, kind, agent, agent tags, or your metadata.

<Tabs>
  <Tab title="Console">
    In the sidebar, click **Sessions**. The **Status**, **Agent**, **Agent tags**, and **Metadata** filters cover every session. The search box matches id, agent, state, or metadata on the page shown; paste a full session id to find a session on any page. Click a column header to sort the page.
  </Tab>

  <Tab title="cURL">
    ```bash theme={"theme":"css-variables"}
    curl 'https://api.recursion.labelbox.com/managed-agents/v1/sessions?status=failed&metadata=customer_id:acme-042&root_only=true&limit=50' \
      -H "Authorization: Bearer $RECURSION_API_KEY"
    ```
  </Tab>
</Tabs>

`root_only=true` returns one row per run, without the subagent sessions under it. List rows are summaries: they leave out snapshots, `config` is empty, and token totals aren't included. Each row carries the session tree's `costUsd` and `costState` (see [Usage and cost](/recursion/usage-and-cost)).

<Accordion title="List parameters and paging">
  | Parameter | Description |
  | - | - |
  | `status` | One status, such as `failed`. |
  | `kind` | Comma-separated kinds, matched as OR: `chat`, `subagent`, `session_analyst`. Analyst sessions appear only when you name `session_analyst`. |
  | `agent_id` | Sessions of one agent. |
  | `tag_ids` | Comma-separated agent tag ids, up to 32. A session matches only if its agent currently has every tag. An unknown tag id returns `404`. |
  | `metadata` | `key:value`, repeatable up to 32 times. All pairs must match. The value is everything after the first colon. Child sessions of a matching root also match, so add `root_only=true` for one row per run. |
  | `metadata_key` | A key that must be present, repeatable up to 32 times. |
  | `root_only` | `true` returns root sessions only. |
  | `limit` | Page size. Default 100, maximum 1000. Larger values are reduced to 1000. |
  | `page_token` | The `next_page_token` from the previous page. |

  A page token is valid for one hour and only with the same filters and `limit`. Changing either returns `400 invalid_request`; start again without a token. Because the list is ordered by last update, a session that changes while you page can appear twice or be skipped. Deduplicate by `session_id`, or filter on a value that does not change.
</Accordion>

## How a session runs

Two fields describe a session. `status` says whether it's still live. While it's `active`, `execution_state` says what the agent loop is doing. When the loop isn't running, `stop_reason` says why.

| `stop_reason` | Meaning | What to do |
| - | - | - |
| `end_turn` | The agent finished its turn with nothing left to do. | Read the final message and deliverables. |
| `requires_action` | The session was stopped for review after failing the same way repeatedly, or it is waiting on a browser hand-off (`status: awaiting_human`). | After a review stop, fix what blocked it, then reply to continue. |
| `sleeping` | The agent chose to wait. The session stays `active`. | Nothing, or send a message to wake it before `wake_at`. |
| `awaiting_subagents` | A coordinator is waiting for subagents to report back. | Nothing. See [Multi-agent sessions](/recursion/multi-agent). |
| `out_of_credit` | Your tenant ran out of credit. The session paused before its next model request. Requests already in flight for the tenant complete and are charged, so the balance can end slightly below zero. Its subagents pause the same way. | Buy credits. A session paused in the last 72 hours continues on its own within about a minute, subagents included; send a message to continue it sooner or after that. See [Billing](/recursion/billing#when-credits-run-out). |
| `tenant_suspended` | Your account is suspended. The session paused without finalizing its work. | Contact support to restore the account, then send a message to continue. |
| `interrupted` | Someone interrupted or cancelled the run. | Send a message to continue. |
| `error` | The loop stopped on an error. | Read `failure`. |

<Accordion title="Statuses and execution states">
  | `status` | Meaning |
  | - | - |
  | `active` | The session is live. Check `execution_state` for what it is doing. |
  | `completed` | The agent loop finished. This does not by itself mean the task succeeded. See [What success means](#what-success-means). |
  | `failed` | The session stopped on an error. The `failure` object explains it. |
  | `cancelled` | Someone cancelled the session. Its sandbox is torn down. |
  | `awaiting_human` | The agent asked a person to handle a step in its browser display, such as a sign-in, which can happen only when the environment has computer use on. `active_handoff` describes the request. The agent resumes when you send a `handoff_resolved` event (see [Events](/recursion/events)), or on its own when the hand-off deadline passes: 30 minutes unless the agent set another, at most 4 hours. Interrupting or cancelling the session also ends the hand-off. `execution_state` is `idle` with `stop_reason: requires_action`. |

  | `execution_state` | Meaning |
  | - | - |
  | `provisioning` | The sandbox is starting. This normally takes under a minute. After 5 minutes the session fails with `sandbox_provision_timeout`. |
  | `queued` | Waiting for a free slot under the agent's `max_concurrent_sessions`, or for sandbox capacity. |
  | `running` | The agent is working. |
  | `idle` | The loop is not running. `stop_reason` is always set. |

  `provisioning`, `queued`, `running`, and `idle` are execution states of a session whose `status` is `active`. `completed`, `failed`, and `cancelled` are terminal statuses: the session does not change on its own after reaching one. A follow-up message to a root session in a terminal status starts it again from `provisioning`, or from `queued` when the agent is at its cap. After a cancel, the session gets a new sandbox. After a completed or failed run, the sandbox is reused when possible.

  When a root session is stopped for review and no subagent is working, its sandbox is stopped with `/workspace` preserved, and restored from `/workspace` before the agent's next tool call; processes it started are gone. A session whose setup ran on its own compute keeps its sandbox until the environment's idle stop.
</Accordion>

<Accordion title="Every state transition">
  | From | When | To |
  | - | - | - |
  | Start | The start request is accepted | `provisioning`, or `queued` when the agent is at its `max_concurrent_sessions` cap |
  | `queued` | A slot or sandbox capacity frees up | `provisioning` |
  | `provisioning` | The sandbox is ready | `running` |
  | `provisioning` | No sandbox capacity is free yet | `queued` |
  | `provisioning` | The sandbox isn't ready within 5 minutes | `failed` |
  | `running` | The run is interrupted, the agent sleeps or waits for subagents, the session is stopped for review, or your tenant runs out of credit | `idle`, with a `stop_reason` |
  | `running` | The agent finishes its work | `completed` |
  | `running` | The agent asks a person for a browser hand-off | `awaiting_human` |
  | `running` | An unrecoverable error | `failed` |
  | `idle` (`sleeping` or `awaiting_subagents`) | A message, the wake time, or a subagent's report | `running` |
  | `idle` (any other `stop_reason`) | A follow-up message | `provisioning`, or `queued` at the cap |
  | `awaiting_human` | A `handoff_resolved` event or the hand-off deadline | `running` |
  | Any live state | Cancel | `cancelled` |
  | `completed`, `failed`, `cancelled` | A follow-up message to a root session | `provisioning`, or `queued` at the cap |
</Accordion>

## What can go wrong

The most common problems:

* **The session stays `queued`.** The agent is at `max_concurrent_sessions`, or a slot freed while your tenant was out of credit. A queued session starts only when a slot is free and credit is available. Wait, cancel other sessions, raise the agent limit, or add credits.
* **`422 environment_not_verified` on start.** The environment's setup script has not passed a current setup run. Run environment setup, then start again. See [Environments](/recursion/environments).
* **`status: completed` but the task is not done.** The loop finishing does not mean the task succeeded. Read the final message, then send a follow-up with what is missing.

<Accordion title="Every session error">
  | Symptom or code | Cause | Fix |
  | - | - | - |
  | `400 invalid_request` on `Idempotency-Key` | The header is missing, malformed, or sent twice. | Send exactly one key of 1 to 256 visible characters. |
  | `409 idempotency_conflict` | The key was used for a different request. | Use a new key for different work. |
  | `409 idempotency_in_progress` | The first request with this key is still running. | Wait for `Retry-After`, then retry with the same key. |
  | `404 not_found` on start | An agent, environment, vault, or referenced session is missing or not visible to you. | Check each id and your access. |
  | `400 invalid_request` on `metadata` | Too many entries, a bad key, or an empty or too-long value. | Keep to 32 entries and the key and value rules in **All start fields**. |
  | `400 invalid_request` on `page_token` | The token expired or the filters changed. | List again without a token. |
  | The session stays `provisioning`, then fails with `sandbox_provision_timeout` | The sandbox did not start within 5 minutes. | Start again. If it repeats, check the environment. |
  | A poller never returns | The session is interrupted or sleeping and stays `active`. | Stop on `idle` states you care about, or send a message. |
  | `409 conflict` when messaging or deleting | A cancel is still finishing. | Retry after a few seconds. |
  | `409 conflict` when messaging a subagent | Subagent sessions in a terminal status do not resume. | Message the root session. |
  | `429 rate_limit_exceeded` | Too many requests. | Wait for `Retry-After`. Retry only when the operation's retry strategy in **Endpoints** permits replay. |

  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 on metadata, referenced sessions, concurrency, and provisioning are in [Limits](/recursion/limits).

## Next steps

<CardGroup cols={2}>
  <Card title="Events" icon="wave-pulse" href="/recursion/events">
    Stream, send, and read every kind of session event.
  </Card>

  <Card title="Session operations" icon="gauge" href="/recursion/session-operations">
    Search metadata, inspect compute, ask the analyst, and read the tree.
  </Card>

  <Card title="Files" icon="file" href="/recursion/files">
    Give a session input files and download its outputs.
  </Card>

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