Sessions
A session is one run of an agent in an environment. It holds the transcript, the sandbox and the status, and it keeps running after your request returns.
const session = await client.beta.sessions.create({
agent: agent.id,
environment_id: environment.id,
title: "PR 412",
initial_events: [
{
type: "user.message",
content: [{ type: "text", text: "Review PR 412." }],
},
],
});Fields
| Field | Type | Notes |
|---|---|---|
| agent | string or object | An agent id, a pinned version, or an agent with overrides. |
| environment_id | string | Required. |
| title | string | For your own use. |
| metadata | object | String to string. Echoed, never read. |
| budget | object | A cap on the session's list cost. See Budgets. |
| initial_events | array | Events applied at creation. A message here starts the first turn. |
The agent snapshot
The session resolves the agent once, at creation, and carries the result as agent on every read. A later update to the agent does not reach it. Name a version, or override the prompt, the model, the tools or the skills, as described under Agents.
POST /v1/sessions/:id with agent replaces the session's tools or mcp_servers, the only two fields it takes; null clears one. The next turn runs with them, and the session logs a session.updated event.
Status and usage
| Status | Meaning |
|---|---|
| running | A turn is running, or a message is queued and nothing is holding it. |
| idle | The agent has nothing more to do, or it is parked. Send a message and it runs again, unless it stopped at its budget. |
| terminated | The session is finished. It takes no more events. |
Every read also carries usage: input, output and cache tokens, server_tool_use, active_seconds and list_cost. Token counts move when a turn ends; active time counts while it runs.
list_cost is in cents, rounded on the total. A model call on OpenRouter costs what OpenRouter reports for it; on Anthropic or OpenAI, the provider's list price for its tokens. A web search adds what the search provider charges, and each active second what a sandbox costs, whether or not a tool ran in one. A web fetch adds nothing.
Provider keys
Creation resolves a provider key for the agent's model. When no key on the organization serves it, coreloop refuses the session with invalid_request_error and creates nothing.
Every turn resolves the key again. A turn that finds none does not retry: the session emits session.error with the provider it wanted, then parks on session.status_idle with a stop reason of retries_exhausted. It reads idle while it waits, even with a message queued. Add a key that serves the model and every parked session on the organization wakes and carries on.
A model call the provider refuses ends the turn with session.error in the provider's words, typed by its status (billing_error, model_rate_limited_error, model_overloaded_error or model_request_failed_error), then session.status_idle with retries_exhausted. Messages queued behind it stay listed with no processed_at. The next event calls the model again on the history first, then takes each queued message with its own call.
A rate limit, an outage or a stream that breaks is retried instead. Each refused try emits session.error with a retry_status of retrying, and the session stays running in one model request span. The wait is the provider's retry-after, up to 15 minutes; without one, 1 second, doubling up to a minute. A message sent during the wait is taken at the next try. An interrupt ends the wait at once: the span ends failed and the session idles with end_turn. After eight tries the last error carries exhausted and the session idles with retries_exhausted.
Past 86% of the model's context window, the history before the call is summarized: agent.thread_context_compacted follows the summary's span, and the call reads the summary in place of what it replaced. A call the provider refuses as too long for the window ends the session instead: session.error with unknown_error and a terminal retry status, then session.status_terminated.
Archive and delete
POST /v1/sessions/:id/archive takes the session out of the list and closes it to new events. The transcript still reads, and the stream still opens.
DELETE /v1/sessions/:id is a soft delete. The session reads as absent afterwards.
Budgets
A session created with budget caps its list cost: {"type": "limit", "max_list_cost": {"amount": "500", "currency": "USD"}}, in whole cents. The cap is checked before each model request. The request that crosses it finishes, and so do the tools it calls. The session then goes idle with budget_reached, after a session.usage that carries the budget.
At the cap, a send is a 400 unless it only answers work under way: a tool confirmation, a tool result, or an interrupt. An update that raises the cap above the cost so far, or sets budget to null, resumes the paused turn. A budget is set only on create, and removing it is permanent.
Endpoints
- POST/v1/sessions
- GET/v1/sessions
- GET/v1/sessions/:id
- POST/v1/sessions/:id
- POST/v1/sessions/:id/archive
- DELETE/v1/sessions/:id