SessionSchema. API detail: Sessions, Events.
create
POST /v1/sessions. Creates and starts, with the initial message, in one call.
string
required
The agent to run.
string
required
The initial instruction.
number
Pin an exact version; default is the agent’s current one.
string
Context-window selection.
integer
Spend cap for this session, integer micro-USD.
string
The persona this task acts as — an
idn_ id or the persona’s name. Answered as identity_id on the session.object
JSON schema the final document must match.
boolean
Refuse a would-be clean ending that produced no document matching
output_schema.string
Set when a deployment fired this run.
object
Arbitrary key/value pairs.
list
GET /v1/sessions. SessionFilter adds agent_id, status, stop_reason, deployment_id, parent_session_id and handoff_from to the page query. handoff_from is a session id and lists the top-level sessions that session handed work to, newest first.
stop_reason is how you find the sessions a person has to answer. A session held on an ask tool call is status: "idle" like every finished one — what separates it is stop_reason: "awaiting_approval", with the blocked call in pending_actions:
stop_reason: "awaiting_answer" is the same query for the sessions asking you a question rather
than for permission — see answer. "budget_paused" is the same question asked about
money, and "error" about failure.
get
GET /v1/sessions/{id}.
send
POST /v1/sessions/{id}/messages. Sending to a running session is refused with session_running unless you say which of two things you mean: interrupt: true steers it mid-run; queue: true holds the message in the session’s inbox and it becomes the input of the turn that follows. On an idle session queue is a plain send. The two together are validation_failed.
handoffs
POST /v1/sessions/{id}/handoffs. Hands message from session {id} (the sender, a top-level session of yours) to each named agent, starting a top-level session per target marked metadata.handoff = { from_session, from_agent, key, depth }. Who may be named is the sender’s agent’s handoffs policy — the same admission the model’s send_to_agent goes through. A target that is refused is a row in refusals, not a thrown error; the batch resolves for the rest.
client.sessions.list({ handoff_from: root.id }); the work it delegated in-thread is threads.
threads
GET /v1/sessions/{id}/threads. The child sessions a coordinator delegated from {id} with send_to_agent wait: true — parent_session_id is {id}, and they ran on its budget. A handoff (wait: false, or handoffs above) is not here: it is a top-level session of its own, found by handoff_from.
interrupt
POST /v1/sessions/{id}/interrupt. Stops the current turn; an optional message tells the agent why.
confirmTool
POST /v1/sessions/{id}/tool_confirmations. Answers a session paused on a gated tool call. It
carries a verdict and no payload, so it cannot answer a question — that is answer, below, and
this route refuses a kind: "question" row rather than folding “allow” back at an agent that asked
for a mailbox address.
scope says how far the allow reaches. Omitted (or "call") it approves this one call.
"session" grants the call’s tool for the rest of that session — every later call to the same
tool name runs with no confirmation — and nothing else: one tool name, one session, gone when the
session ends, never written onto the agent, and never valid on a deny. For a call through the
tool invoker (tools_execute) the granted tool is the one named in its tool argument: the same
target runs with no confirmation, any other target through the invoker is still confirmed.
answer
POST /v1/sessions/{id}/answers. Answers a session parked on a question it asked you.
A session with stop_reason: "awaiting_answer" carries a kind: "question" row in
pending_actions, holding the sentence the agent wrote and one to three fields. answers is keyed
by those fields’ keys; a value is a string, or an array of them for a multiple choice:
optional, which
is answered by leaving the key out entirely rather than by sending an empty string — and no other, so
a wake can never find the thing
the agent stopped for still missing. A choice answer is not limited to that field’s options:
other defaults to true precisely so a person can say what the agent did not think of.
answer
POST /v1/sessions/{id}/answers. Answers a session parked on a question (stop_reason: "awaiting_answer"), keyed by the question’s fields[].key. A sibling of confirmTool, not a variant: a held tool call takes a verb, a question takes a payload.
cancel
POST /v1/sessions/{id}/cancel. Terminal — the session cannot be resumed.
setBudget
PATCH /v1/sessions/{id}/budget, body { cap_micro_usd }. Raise only — a cap at or below what is already consumed would be retroactive and is refused. Integer micro-USD.
events
GET /v1/sessions/{id}/events — the same log client.stream serves, read as pages. after_seq is exclusive, so polling with the last seq you saw never re-reads it.