Agent API
Reference the Agent SDK and REST contracts for start, status, history, reconnect, tools, and completion.
Overview
Durable agents use POST /v2/runner/{deployment_id}/conversation for start, continue, and resume. Reconnect reads use the existing GET /v1/runner/conversation/{conversation_id}/messages endpoint.
Use the SDK unless you need direct HTTP control. client.agent and useAgent() manage refresh, capability advertisement, and matching result submission.
Agent SDK
client.agent is the dedicated SDK controller for agent conversations. It uses the REST endpoints documented below without introducing a separate agent resource or agent ID; conversationId is the durable identifier.
| Method | Returns | Behavior |
|---|---|---|
start(options) | AgentState | Starts an agent turn and opens synchronized state |
get(conversationId, options?) | AgentState | Reads one snapshot without polling |
open(conversationId, options?) | AgentState | Loads the latest page and keeps state synchronized |
loadMoreHistory(options?) | AgentState | Prepends the next older agent-history page |
loadMoreMessages(options?) | AgentState | Compatibility alias for loadMoreHistory |
continue(options?) | AgentState | Starts another turn on the open conversation |
resolveTool(callId, output) | AgentState | Submits results once every call in the pending round is resolved |
subscribe(listener) | unsubscribe function | Notifies a vanilla JavaScript consumer when open state changes |
getState() | AgentState | Reads the current in-memory controller state |
close() / abort() | void | Stops SDK polling and local work |
AgentState
| Field | Type | Meaning |
|---|---|---|
conversationId | string | null | Durable conversation identifier |
conversationStatus | string | null | active or permanently completed |
runId | string | null | Current run identifier for observability; never required as input |
runStatus | AgentRunStatus | null | Current execution status |
messages | ConversationMessage[] | Actual user and assistant messages only |
history | AgentHistoryItem[] | Chronological messages, tools, actions, and client-tool activity |
historyPageInfo | object or null | Opaque nextCursor and hasMore |
messagesPageInfo | object or null | Message-only compatibility view derived from history |
pendingToolCalls | AgentPendingToolCall[] | Current unresolved client tools |
toolSummary | AgentToolSummary[] | Safe tool/action names and statuses |
progress | AgentProgress[] | Safe current-run progress summaries |
status | AgentStatus | SDK lifecycle status |
error | SDK error or null | Latest local/API error |
start() accepts both triggerId and conversationId. Use triggerId for trigger-created work and include conversationId only when continuing the conversation already linked to that trigger.
POST /v2/runner/{deployment_id}/conversation
The request body selects one of three operations:
| Operation | Required fields |
|---|---|
| Start | content or a trigger_id containing content |
| Continue | conversation_id, content |
| Resolve pending tools | conversation_id, tool_results |
Request fields
| Field | Type | Required | Description |
|---|---|---|---|
content | MessageContent[] | Start without trigger, or continue | Non-empty input; a start may omit it when trigger_id supplies stored content |
conversation_id | UUID | Continue or resume | Existing active conversation |
variables | object of strings | No | Initial conversation variables |
meta | object | No | Initial searchable metadata |
history_limit | integer | No | Prior message pairs supplied to the model; default 5 |
client_capabilities | array | No | Tool names or { "name", "input_schema" } objects connected for this request |
tool_results | array | Resume | One result for every pending call |
trigger_id | string | No | Prepared trigger for the initial turn |
All v2 conversations use durable semantics. Pending client actions remain available for reconnect, and the backend finds the current pending run from conversation_id. When Allow conversation completion is enabled, complete_conversation is available to the model.
Autonomous backend start
Omit client_capabilities when no connected client tools are available:
curl https://api.amarsia.com/v2/runner/YOUR_DEPLOYMENT_ID/conversation \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{
"content": [{ "type": "text", "text": "Analyze today'\''s support escalations." }],
"variables": { "TEAM": "enterprise" },
"meta": { "jobId": "job_8421" }
}'A completed turn can return:
{
"run_id": "5af940ce-8610-42d9-891f-f3f20685fbfa",
"run_status": "completed",
"conversation_id": "8bd6d8ca-fc71-4d46-858b-e50b4a9bf83b",
"conversation_status": "active",
"completed_at": null,
"name": "Support escalation review",
"content": "Three escalations require follow-up.",
"input_tokens": 44,
"output_tokens": 18,
"response_time": 1.42
}run_status: "completed" means this run finished successfully. conversation_status: "active" means another run is allowed. Every v2 run returns a run_id for observability, but clients persist and submit only conversation_id.
run_status is the execution lifecycle:
running: the model is executing.waiting_for_client_action: every item inclient_tool_callsmust be resolved.completed: the run finished successfully.interrupted: accepted client results were saved, but continuation was interrupted.failed: the run failed.expired: a legacy non-agent pending run expired.
conversation_status is separate and is either active or permanently completed.
Continue an active conversation
curl https://api.amarsia.com/v2/runner/YOUR_DEPLOYMENT_ID/conversation \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{
"conversation_id": "8bd6d8ca-fc71-4d46-858b-e50b4a9bf83b",
"content": [{ "type": "text", "text": "Continue." }],
"client_capabilities": ["ask_user"]
}'If the model calls a connected or always tool, the response pauses:
{
"run_status": "waiting_for_client_action",
"conversation_id": "8bd6d8ca-fc71-4d46-858b-e50b4a9bf83b",
"conversation_status": "active",
"completed_at": null,
"run_id": "d889fae8-a888-45e7-a9bd-ee0cb4baf200",
"client_tool_calls": [{
"call_id": "call_1",
"name": "ask_user",
"arguments": {
"questions": [{
"id": "goal",
"question": "What is your goal?",
"input_type": "single_select",
"options": ["Grow revenue", "Reduce costs"]
}]
}
}],
"progress": [{
"type": "client_action_required",
"call_id": "call_1",
"name": "ask_user",
"summary": "What is your goal?"
}]
}Resolve ask_user
Resume through the same endpoint with every pending call_id:
curl https://api.amarsia.com/v2/runner/YOUR_DEPLOYMENT_ID/conversation \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{
"conversation_id": "8bd6d8ca-fc71-4d46-858b-e50b4a9bf83b",
"client_capabilities": ["ask_user"],
"tool_results": [{
"call_id": "call_1",
"output": {
"answers": [{ "id": "goal", "value": "Grow revenue" }]
}
}]
}'Text and single-select answers require a non-empty string. Multi-select answers require an array of strings, and every requested question ID must appear exactly once.
Resolve upload_document
Upload the file first, then send one result for its pending call:
curl https://api.amarsia.com/v2/runner/YOUR_DEPLOYMENT_ID/conversation \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{
"conversation_id": "8bd6d8ca-fc71-4d46-858b-e50b4a9bf83b",
"client_capabilities": ["upload_document"],
"tool_results": [{
"call_id": "call_upload",
"output": {
"content": [{
"type": "url",
"mime_type": "application/pdf",
"file_uri": "https://files.example.com/agreement.pdf"
}]
}
}]
}'content must contain at least one non-text file part with mime_type and file_uri, or supported inline data content.
Advertise a custom connected tool
Configure lookup_customer in the dashboard with when_connected availability. Advertise its optional schema for this request:
curl https://api.amarsia.com/v2/runner/YOUR_DEPLOYMENT_ID/conversation \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{
"content": [{ "type": "text", "text": "Check whether customer cus_123 is active." }],
"client_capabilities": [{
"name": "lookup_customer",
"input_schema": {
"type": "object",
"properties": {
"customerId": { "type": "string" }
},
"required": ["customerId"]
}
}]
}'When the response requests lookup_customer, execute it in your client and resume with its call_id and an object output. The dashboard stores name, description, and availability; it does not require a schema.
Create an emailed always flow
Configure upload_document as always, then create a hosted-agent trigger:
curl https://api.amarsia.com/v1/runner/YOUR_DEPLOYMENT_ID/trigger \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{
"content": [{ "type": "text", "text": "Collect the signed service agreement." }],
"variables": { "CUSTOMER_ID": "cus_123" },
"ui_surface": "agent",
"ttl_hours": 72
}'Use the returned trigger id in:
https://chat.amarsia.com/agent/YOUR_DEPLOYMENT_ID?tid=RETURNED_TRIGGER_IDThe API can also start directly from the trigger without repeating its content or variables:
curl https://api.amarsia.com/v2/runner/YOUR_DEPLOYMENT_ID/conversation \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{
"trigger_id": "RETURNED_TRIGGER_ID",
"client_capabilities": ["upload_document"]
}'The backend validates the trigger and uses its stored content and variables. Explicit request content or variables override the corresponding trigger values. The hosted route starts or restores the conversation and writes its durable cid into the URL.
GET /v2/runner/{deployment_id}/conversation/{conversation_id}/history
This agent-specific endpoint returns one chronological timeline without changing the existing v1 chatbot history contract:
curl "https://api.amarsia.com/v2/runner/YOUR_DEPLOYMENT_ID/conversation/CONVERSATION_ID/history?limit=50" \
-H "x-api-key: YOUR_API_KEY"items can contain message, tool, action, client_tool, and conversation_completed entries. Each item has a stable id and created_at. Pass next_cursor back as cursor to load older history; treat it as opaque. A null cursor means no older activity remains.
GET /v1/runner/conversation/{conversation_id}/messages
This existing chatbot endpoint remains unchanged. Agent SDKs use the v2 history endpoint instead:
curl "https://api.amarsia.com/v1/runner/conversation/8bd6d8ca-fc71-4d46-858b-e50b4a9bf83b/messages?page=1&page_size=100" \
-H "x-api-key: YOUR_API_KEY"| Query field | Type | Default |
|---|---|---|
page | integer | 1 |
page_size | integer | 20 |
{
"items": [{
"id": 301,
"role": "user",
"content": [{ "type": "text", "text": "Collect the onboarding details." }],
"created_at": "2026-07-11T15:10:00Z"
}],
"total": 1,
"page": 1,
"page_size": 100,
"has_more": false,
"conversation_status": "active",
"completed_at": null,
"run_id": "d889fae8-a888-45e7-a9bd-ee0cb4baf200",
"run_status": "waiting_for_client_action",
"pending_client_actions": [{
"call_id": "call_1",
"name": "ask_user",
"arguments": {
"questions": [{
"id": "goal",
"question": "What is your goal?",
"input_type": "text"
}]
}
}],
"tool_summary": [{
"name": "lookup_customer",
"status": "completed"
}],
"progress": [{
"type": "client_action_required",
"call_id": "call_1",
"name": "ask_user",
"summary": "What is your goal?"
}]
}items contains actual stored messages only. Pending client tools, tool usage summaries, and safe progress are separate fields; linked server actions remain action logs.
Reconnect directly
- Read every message page you need to render.
- Stop writes if
conversation_statusiscompleted. - Render
items,progress, andtool_summaryseparately. - If
pending_client_actionsis non-empty, collect an output for every call and submit it withconversation_id. - Refresh after
409because another client may have accepted the results. - If
run_statusisinterrupted, start a new agent turn with"Continue."; do not replay accepted results.
Two browsers can read the same pending call. The first accepted matching result set wins, an identical retry is idempotent, and different outputs for the same call IDs conflict.
Completion
When Allow conversation completion is enabled, the model can call complete_conversation. A request can ask the assistant to close resolved work:
curl https://api.amarsia.com/v2/runner/YOUR_DEPLOYMENT_ID/conversation \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{
"conversation_id": "8bd6d8ca-fc71-4d46-858b-e50b4a9bf83b",
"content": [{ "type": "text", "text": "Everything is resolved. Close this case." }]
}'The model requests completion, but the backend applies it only after the round ends, with no pending client action, and only when complete_conversation was the final executed server tool. Premature requests are logged as not_completed and leave the conversation active. Check conversation_status and completed_at in the response or messages read. Completion is permanent, does not delete stored data, and later start, continue, or resume attempts return 409 conversation_completed.
Errors and concurrency
| Code | Status | Meaning |
|---|---|---|
missing_tool_results | 400 | One or more pending call IDs have no result |
unknown_call_id | 400 | A result references a non-pending call |
invalid_tool_result | 400 | A built-in or custom output is invalid |
client_action_required | 409 | A new turn was attempted while input is pending |
run_in_progress | 409 | A new turn was attempted while a run is active |
action_result_conflict | 409 | Different outputs were already accepted for these call IDs |
conversation_completed | 409 | The conversation is permanently completed |
run_expired | 409 | A legacy non-agent pending run expired |