Retrieve a session
List sessions
Results are paginated. Filter by agent or bydeployment_id (each scheduled fire is a session tagged with its deployment), and sort by creation time. Each response includes has_more and a next_cursor; pass next_cursor back as after to fetch the next page. The cursor is null at the end.
Sending input
You drive a session by sending it input. Every input is recorded on the event stream, and if the session isidle it wakes into running. There are four input types:
fromSeq to resume from the last seq you handled without gaps or duplicates. See Events & streaming.
Interrupt and redirect in one call
Inputs can be batched so they land atomically. The common case is steering a running agent: send an interrupt together with a new message. The agent wraps up the current turn cleanly at the next commit boundary — the interrupted turn ends withstop_reason: "end_turn" — and then picks up the redirect.
Awaiting-approval loop
When the agent calls a tool governed by anask policy, the runtime does not run the tool. Instead the session goes idle with stop_reason: "awaiting_approval" and exposes the waiting call(s) on pending_actions[]. You resolve each one with an allow/deny confirmation; once the last pending action is answered, the session resumes automatically.
ask, and Events & streaming for the tool.confirm event shape.
Awaiting-answer loop
The mirror of the loop above, for the park the agent starts itself. When an agent callsask_operator the turn stops the same way — idle, rows on
pending_actions[], no budget burning — but the row is tagged kind: "question" and carries a
question object: the sentence the agent wrote, and one to three fields to fill in.
- Render from
question, never fromargs. They hold the same content —argsis what the model called with and what replays byte-identically — butquestionis the shape a renderer reads, and it is the one that will keep its meaning if the tool’s arguments ever change. - Every field must be answered, with a non-empty value, unless it is marked
optional: an agent that could proceed without one of them would not have asked it. A partial answer isvalidation_failed(400) naminganswers.<key>. The escape a person needs is the free-text arm, which is on by default. - A field with
optional: truemay be skipped, by leaving its key out. That is the only way to say “no answer”: omit the key entirely. A key that is present is checked like every other one, so sending"",[]or an unlisted option for an optional field is stillvalidation_failed— an answer that was given is an answer that was meant. - A
choiceanswer is the option’s words, never an index. The model reads them back as prose. Sendstring[]for amultiple: truefield and a plainstringfor everything else; the route enforces the arity.
Not every agent can ask.
ask_operator is offered only on a harness that can hold a call open for a person — see Harness capabilities — and an agent whose toolset denies it cannot park this way at all.Approver
Each resolved confirmation records who answered it — the approving principal (a user or API key id) is written to the event and audit trail alongside the decision and any deny reason. This is what lets you prove, after the fact, that a payout or destructive action was signed off by a person rather than silently auto-run. An unansweredask waits until someone answers it. Watch for session.idle with stop_reason: "awaiting_approval", or run vetta session inbox, so every parked call reaches a person.
Plan mode
Send withmode: "plan" — or create the session with it — and the session plans before it builds. It researches read-only, asks what it cannot look up, and keeps a living Markdown plan with - [ ] to-dos. When the plan is ready it proposes it, and the session parks on one card: allow it to build the plan, or deny it — with an optional reason — to keep planning.
- Read-only is enforced, not asked for. In plan mode the agent is offered reads, web search, the board read,
ask_operatorand the two plan tools. Shell, file writes, the browser, generators, metered tools and anything that starts other sessions are not offered at all. - Only a person leaves plan mode. Allow on the plan card, or a send with
mode: "build". The agent has no way out. - The plan is its own record. Read it with
GET /v1/sessions/{id}/plan. Edit it withPUT /v1/sessions/{id}/planand thebase_versionyou read; a stale version isversion_conflict. The agent sees the edit on its next turn, and after the allow it ticks each to-do as it builds. - The stream says so.
session.modemarks each change, andplan.updatedcarries the plan on every write. Both are opt-in on the events list (include_accepted=true).
mode: "plan" there is validation_failed.
TypeScript
Interrupting
Arunning session must be interrupted before you can update its agent, archive it, or delete it. An interrupt is delivered as an event and stops the current turn at the next commit boundary — you lose at most one turn, never the run. On its own, an interrupt lands the session at idle with stop_reason: "interrupted".
Archiving
Cancelling stops a session and releases its sandbox while preserving its record and its events.CLI
Next: events & streaming
The resumable event model behind every session.