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:
Idempotency-Key. A retried send without one is a
second message.
Finding the turn your message produced
The reply carries noturn_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:
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 continuesalready_resolved— this call was answered before; the second answer is discarded rather than appliednot_delivered— nothing was waiting for it
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.
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.
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
- required-actions.md — the calls you answer with
input.tool_result - events.md — watching what the agent does with what you sent
- idempotency.md — retrying a send without sending twice