Skip to main content
Everything you can send a session, in one place. All four 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.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