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

FieldTypeNotes
agentstring or objectAn agent id, a pinned version, or an agent with overrides.
environment_idstringRequired.
titlestringFor your own use.
metadataobjectString to string. Echoed, never read.
budgetobjectA cap on the session's list cost. See Budgets.
initial_eventsarrayEvents 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

StatusMeaning
runningA turn is running, or a message is queued and nothing is holding it.
idleThe agent has nothing more to do, or it is parked. Send a message and it runs again, unless it stopped at its budget.
terminatedThe 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