Chats
get_chat
Section titled “get_chat”Get a chat’s current state — status, full message history, recorded
outcomes, cost, and any pending human-input request. Poll this until
status is no longer “running”.
The messages history holds only user/assistant text turns. Human
input requests and the answers they got are NOT messages — they come
back in hitl_requests (every request for the chat, with its
response_text), while pending_hitl_request holds only the one still
waiting. When the request offered ui (buttons/choice/text/info —
the same shape request_human_input accepts), it’s included on both
so you can see exactly what a human would be asked to pick from
before calling respond_to_hitl.
When the agent recorded an outcome, its summary IS the answer, and the
last assistant message carries it (tagged kind: "outcome_summary")
even if the model’s own closing turn was empty — so messages alone is
enough to read the result. outcomes remains the graded record.
stop_reason says why a chat is no longer moving: “cost_limit” (raise
the agent’s per-chat budget and start again), “awaiting_input” (a human
must answer), “timer” — the agent set its own timer and sleeps until
waiting_until, so it is still working and nobody need act —, “queued”
(waiting for a free slot), “awaiting_children” (waiting on chats it
spawned) and “environment_offline” (its remote machine is not connected;
the turn starts by itself when the machine comes back), all likewise
self-resolving, “model_unpriced” — the model the chat
runs on has no price we can charge credits against, so the turn was refused
before it was sent; the organisation’s balance is untouched and topping up
changes nothing, so move the agent to another model or ask support to price
this one. That stop and “credits_exhausted” are both continuable: once
the blocker is lifted, a person can carry the same chat on from where it
stopped rather than starting over —, “goal_failed” — the agent recorded a failed goal and
closed the chat itself, so its status is “errored” by decision rather
than by breakage and the summary says why — or “no_outcome”, the agent
replied and stopped without calling goal_complete, so outcomes is
empty and nothing was declared. It is idle, not running: stop polling
and treat the result as unverified.
Pass include_trace=true to also get a trace array of the underlying
events (tool calls + results with name/args/result preview), oldest
first — useful to see why the agent did something or which tool failed.
| Parameter | Type | Required | Description |
|---|---|---|---|
| chat_id | string | yes | |
| include_trace | boolean | no | |
| org | string | no |
list_chats
Section titled “list_chats”List recent chats for an agent (by id or slug), newest first.
Paged: the response carries total, has_more and next_offset —
pass next_offset back as offset to walk the rest.
status alone never means “finished”. Chats have no terminal
“completed” status — a chat that ran to a successful goal_complete
still reports status: "active", indistinguishable from one still
working. To tell them apart, use the same two fields get_chat
exposes:
outcome_status— the chat’s rolled-upoutcomesstatus (“success” / “failed” / “partial”), ornullif it hasn’t recorded one yet. When a chat recorded more than one outcome, anysuccesswins over an earlier failure/partial; otherwise the most recently recorded outcome’s status is reported.stop_reason— why the chat stopped moving: “cost_limit”, “awaiting_input”, “timer”, “queued”, “awaiting_children”, “environment_offline”, “goal_failed”, “no_outcome” (it replied and stopped without declaring a result), ornullwhile it’s still running. Seeget_chat’s docstring for the full vocabulary.
A chat is actually finished when outcome_status is non-null (or
stop_reason is one of the terminal reasons above) — not merely
because status reads “active”.
| Parameter | Type | Required | Description |
|---|---|---|---|
| agent | string | yes | |
| limit | integer | no | Max rows to return (1–200). |
| offset | integer | no | Rows to skip — pass the previous response’s next_offset. |
| org | string | no |
respond_to_hitl
Section titled “respond_to_hitl”Answer a pending human-input request (from a paused chat) and resume
the agent. Use action (“approve”/“reject”, or one of the request’s
ui buttons) for approvals and button choices, selection for one of
the ui choice options, selections when the request asks more than
one question, text for a free-text answer. At least one is required.
get_chat’s pending_hitl_request.ui always spells out what is on
offer: a request that declared no ui of its own resolves to the default
for its type, so an approval really does offer Approve/Reject. When
the resolved ui lists buttons/choice, action/selection must be
one of those options — a value that isn’t gets rejected with the valid
options listed. A ui that is free-text only accepts anything.
More than one question in one request. Each {"choice": [...]}
element in the resolved ui carries a key (and often a label). To
answer them all, pass selections as {key: chosen_value} —
e.g. {"environment": "Staging", "rollback_window": "24 hours"}. Every
value is checked against its own group’s options. A bare selection is
only accepted when the request offers a single choice group; otherwise it
cannot say which question it answers and is rejected with the keys and
their options listed. selection and selections are mutually
exclusive — send one or the other, never both.
| Parameter | Type | Required | Description |
|---|---|---|---|
| action | string | no | |
| org | string | no | |
| request_id | string | yes | |
| selection | string | no | |
| selections | object | no | |
| text | string | no |
send_message
Section titled “send_message”Send a user message to an existing chat and dispatch the agent turn.
A message to a paused chat resumes it. Runs asynchronously — poll
get_chat for the result.
To attach files, first use create_upload_url to get presigned upload
URLs, PUT your files to those URLs, then pass the returned upload_ids
in attachment_upload_ids. Each upload_id is single-use: attaching is
all-or-nothing, so if any id has already been attached (or has expired)
the call is refused naming that id, and no message is sent.
| Parameter | Type | Required | Description |
|---|---|---|---|
| attachment_upload_ids | string[] | no | |
| chat_id | string | yes | |
| message | string | yes | |
| org | string | no |
start_chat
Section titled “start_chat”Start a new chat with an agent (by id or slug) and send the first
message. Returns the chat_id; the turn runs asynchronously — poll
get_chat for the result.
By default the chat runs the agent’s published (active) revision.
To test an unpublished instruction without deploying it, pass
use_draft=true (pins the agent’s current draft revision) OR
revision_version=N (pins a specific past version). The pin holds for
the chat’s whole life. The two options are mutually exclusive.
Where the chat runs is environment. Pass an environment’s slug,
id, or unambiguous name — list_environments shows what this org has.
A remote_daemon environment is a machine the user owns: their files,
their installed tools, in place. Nothing else reaches that machine, so a
request to run something on the user’s computer needs this argument;
without it the chat runs where the org’s default puts it, and a command
that only exists on their machine fails there for reasons that look
nothing like the real cause.
sandbox_enabled=true is the narrower, older switch: it turns on our
built-in Linux sandbox — an isolated filesystem + network on our
infrastructure, persisting across the conversation — for substantial
coding, data, or file-heavy work. It only ever adds capability on top of
the agent’s own sandbox setting, and it is not a way to reach any
particular machine.
model_mode_override (‘trivial’ | ‘normal’ | ‘high_effort’, shown as
Trivial / Standard / High) changes the agent’s own model_mode for this
chat only — the one model lever an org whose plan selects effort rather
than models has, in either direction: trivial drops routine work onto
a cheaper model, high_effort buys a dearer one. It is sticky: it
applies to this turn and every later send_message on the chat. Only
meaningful for such an org; an org with direct model choice is refused it
(the value would be inert) and picks a model on the agent instead.
To attach files, first use create_upload_url to get presigned upload
URLs, PUT your files to those URLs, then pass the returned upload_ids
in attachment_upload_ids. Each upload_id is single-use: attaching is
all-or-nothing, so if any id has already been attached (or has expired)
the call is refused naming that id, and no chat is created.
| Parameter | Type | Required | Description |
|---|---|---|---|
| agent | string | yes | |
| attachment_upload_ids | string[] | no | |
| environment | string | no | |
| message | string | yes | |
| model_mode_override | string | no | |
| org | string | no | |
| revision_version | integer | no | |
| sandbox_enabled | boolean | no | |
| title | string | no | |
| use_draft | boolean | no |