Skip to main content
Everything you can send a session, in one place. They all go to the same endpoint, one per request:
The body is always an array of exactly one event. Batching two would need a partial-failure result nothing could act on, so it is refused rather than half-supported.
A success is 202, not 200 — the agent has the work, not the answer. Every refusal arrives in the shape described in errors.md, with the code above in error.code.

input.message

content takes a string, or an array of parts if you prefer the longer form:
Sent while a turn is running, it queues rather than interrupting — see design-decisions.md for why that is not steering. The reply tells you which happened:
Send an Idempotency-Key. A retried send without one is a second message.

Finding the turn your message produced

The reply carries no turn_id, because at the moment it is written there is no turn yet — the agent starts one when it picks the message up, which is later even when nothing was queued. If you watch events or take a webhook, the turn id arrives with them and there is nothing to do. If you poll, there is one trap worth knowing:
Remember the latest turn id before you send, and wait for one that is different:
A fresh session has no previous turn, so a first message needs none of this.

input.context

Something the session should know, without being asked to reply. It lands in the transcript and no turn runs.
The reply carries delivered:
  • true — a running agent took it there and then
  • false — the sandbox was not up
false does not mean it was lost. The text is in the transcript either way, and the next turn is built with it — so a room can hand an agent context before that agent has ever spoken, which is the usual case when someone joins a conversation already in progress. It appears afterwards as an item of type context, and on the stream as user.context. Deliberately not a message: nobody said it to this session, and rendering it as one would show the agent a conversation that never happened. If a turn is already running, the context lands after it finishes. That turn’s context is fixed, and inserting into it would make one turn’s behaviour depend on when a message happened to arrive. Without this, keeping several agents current means holding your own copy of the conversation and resending it — which makes every prompt grow with the length of the room. See agents that work together.

input.tool_result

The answer to a function the agent called. turn_id and call_id are copied from the required action verbatim.
The reply’s outcome is the thing to check:
  • accepted — the turn has it and continues
  • already_resolved — this call was answered before; the second answer is discarded rather than applied
  • not_delivered — nothing was waiting for it
Never put a thrown exception’s message in error. It goes to the model, and a stack trace is an efficient way to put your database host into a prompt. Send a fixed string; log the real one.

input.steer

Change course while a turn is running.
This is the deliberate half of the divergence: a plain message waits its turn, and redirecting work in flight has to be asked for by name. Sent when no turn is running, it is a conflict — there is nothing to steer, and silently turning it into a message would be a different thing than you asked for.

input.cancel

Stop the running turn.
No other fields. The turn settles as cancelled — not failed, because you asked for it, and a failure status would send you looking for a fault that is not there. Cancelling when nothing is running is accepted rather than refused: a caller racing a finishing turn should not have to care which of them won. Work already done stays. Items the turn produced remain readable, and anything published to /workspace/outputs before the stop is still collected.

Next