Skip to main content
Seven choices this API makes that a caller will meet sooner or later, and the reasoning behind each. They are written down because a decision nobody documented gets reported as a bug — and because knowing why a thing behaves as it does is what lets you build on it confidently rather than defensively.

1. A message sent while the agent is busy queues; it does not steer

Send input.message during a running turn and it is accepted and queued. The response says so:
To change what a running turn is doing, say so explicitly:
input.steer with no turn running is a conflict rather than a new turn. Why. Whether a turn is in flight is decided from two signals — the live bridge and the durable session status — because reading only one of them swallowed messages during a sandbox wake-up. Having paid for that certainty, we will not spend it guessing which of two different acts a caller meant.

2. Deleting a session destroys its sandbox

DELETE /v1/sessions/{id} ends the compute. Why. In the Agents API a sandbox can be your own infrastructure, so deleting a session leaves it alone. Here the sandbox belongs to the session and is billed to us; leaving it running would bill someone for a project they deleted. If you want the transcript without the compute, that is what the session already is once idle — lifecycle-worker pauses an idle sandbox and reclaims it at a cap, while items, turns and artifacts stay readable.

3. The event stream is replayable

Reconnect with Last-Event-ID and you get everything persisted since that point. A cursor always names a row that exists. This is more than the API this one is measured against offers, and it is the reason a dropped connection is not a lost conversation. Details in events.md.

4. There is no sandbox-free mode

Every session has a workspace. There is no “just the model” configuration. Why. For a coding agent the workspace is the product, not an accessory. An agent that cannot read a file or run a test is a chat endpoint, and you already have one of those.

5. Bring your own model

The API does not sell inference. A session runs against a model credential your organization connected — Anthropic, OpenAI, xAI, OpenRouter, MiniMax, DeepSeek, Qwen, Kimi, GLM, or any compatible endpoint. There is no provider field, no base_url and no key in any request, because the provider is a property of the credential. agent.model and agent.model_credential_id choose which connected credential; they cannot introduce a new one. Consequence worth stating plainly: model spend is yours, on your account, and we never see the key in a request. Sandbox compute is ours.

6. Human approval and questions are first-class

required_actions carries three types: function_call for your code, and approval and question for a person. An integration can therefore see that a human is holding up a session it cares about, rather than watching it sit in requires_action for no visible reason. All three are answerable through this API — input.approval and input.question_answer for the two that are about a person — and a person can still resolve either in the Console. Which of you gets there first is your product’s decision rather than ours. Why. Gobare had a human-in-the-loop path before it had this API, and hiding it from integrations would have made the API’s picture of a session incomplete.

7. No sub-agents

There is no API for spawning a sub-agent. ApiTurn.subagent_id exists in the shape and is currently always null. Why. The useful version of this lives in the agent runtime rather than in the HTTP surface, and we have not convinced ourselves the API-level version earns its complexity. It is an omission we may revisit, not a design we have rejected.

Smaller things

  • The clone is owed, not done. environment.repo is recorded when the session is created and cloned when its workspace comes up. A 201 means the session exists; environment.repo.clone_error is where a failure appears.
  • environment.branch is refused rather than accepted. The clone reports the branch it landed on; it cannot be told which one to use. Storing the request would be a value that changed nothing.
  • Environment profiles are bind-only. Values are never returned and cannot be set through this API.
  • Errors carry a code, not a type. Sixteen codes, listed in errors.md. Three share 429 and recover differently.
  • There is no client package, and there is a client. We publish run-session.ts to copy rather than a library to install: a package is a second surface to keep in step with the first. It is real code — the end-to-end test runs through the same file — so it cannot quietly stop compiling.
  • An agent is a saved configuration, not a running thing. Creating one starts nothing and costs nothing; a session is what runs. Applying an agent copies it, so editing or deleting one never reaches into work already in flight.
  • Ids are ours. Session ids are UUIDs; turns are turn_…; artifacts are art_5e2b9017c4d63a8f; webhook subscriptions are whsub_6c1e40b9a72d58f3. Do not parse them.
  • Times are milliseconds since the epoch, as numbers.

Next