Runs
A run is one request to an agent. The same run, three ways of getting it back.
Three shapes, one body
All three start endpoints take the same request body and differ only in how the answer reaches you. Choose per call — there is nothing to configure.
POST /v1/agents/{agentId}/runs— stream. Server-Sent Events, token by token, including every tool call. Use it when a human is watching; see Streaming.POST /v1/agents/{agentId}/runs/await— await. One JSON response when the run finishes. Simplest to integrate; the request is open for the whole run, so it suits short work. It answers200even when the run failed, so readstatusanderrorCode:If you hang up before it finishes, the run is stopped and recorded as200 application/json{ "runId": "01936c2f-…", "status": "completed", "message": "Three themes dominate …", "usage": { "inputTokens": 1841, "outputTokens": 612, "totalTokens": 2453 }, "errorCode": null, "failureReason": null, "traceId": null }cancelled— unless you sent anIdempotency-Key: then it finishes, and your retry finds it.POST /v1/agents/{agentId}/runs/submit— submit. Returns202and a run id immediately; the run executes in the background and you read it back withGET /v1/runs/{runId}. Use it for long work and for anything triggered by a webhook, where holding a connection is not an option. A submitted run carries no attachments:attachmentFileIdsis ignored on it. It also takes no per-requestmcpServers, and itsmcpAuthis not kept, so a registered server that needs a token has no token on a submitted run; the202lists those tools intoolWarnings.
All three take an optional Idempotency-Key header: send the same call again with the
same key and no second run starts and nothing is billed twice (Limits, Idempotency keys).
The streaming call is the one to avoid hanging up on: its run carries on whatever your client
does, and nothing re-attaches you to it. The awaited call is the opposite — hang up without an Idempotency-Key and the run is stopped.
The request body
{
"message": "required — what you are asking",
"endUserRef": "your-own-user-id",
"conversationId": "…",
"threadConversationHistory": true,
"model": "…",
"tools": ["web_search"],
"attachmentFileIds": ["…"],
"mcpServers": [ … ],
"mcpAuth": [ { "serverSlug": "…", "token": "…" } ],
"userTimeZone": "Africa/Johannesburg"
}Only message is required (at most 8,000 characters). The rest are worth knowing about:
endUserRef— attributes the run to one of your own users; at most 200 printable ASCII characters. Use an opaque id of your own, never an email, a name or a phone number; it is kept in audit entries for 12 months (see Data and privacy). A run without one is unattributed, and the agent's run-history tools refuse to read for it.conversationId— groups runs into one conversation you can read back (below). On its own it does not replay earlier turns to the agent: each run sees only its own message. A conversation belongs to the API client andendUserRefthat first use it: starting a run under one that already holds someone else's run is a404, so mint a fresh id per chat.threadConversationHistory— settrueto have the agent remember the chat: it is replayed the earlier questions and answers of your runs with the sameconversationIdandendUserRef, never anyone else's. The newest 20 runs and 48,000 characters at most; older runs are left out whole. Needs both fields, or it is a400. A run that pauses for an approval, a question or its background tasks and is resumed gets the same earlier chat back, under the same limits.model— run on this model instead of the agent's. A model id: letters, digits and. _ - : / @, starting with a letter or digit, at most 100 characters. It must have a price on record (model_unpricedotherwise) and satisfy your organisation's privacy policy; see Errors.tools— the complete list of tools for this run; at most 32 names. Leave it out and the run gets the agent's own tools and none of your MCP tools. Send it and the run gets exactly the names you list: to add your MCP tools and keep the agent's own (memory'srecallandremember, web search, and so on), name those too. Each tool's name is on the agent's page in the Console, under Tool approvals. A name the agent does not have, or a handle no enabled server binds, is a400rather than ignored. A run that goes ahead without some of your tools answers withtoolWarnings; see Tools and MCP.attachmentFileIds— files to put in front of the agent, at most 8; see Files.mcpServers/mcpAuth— your own tools, declared per request (at most 16 of each); see Tools and MCP.workspaceId— not used on hosted Amy; leave it out.userTimeZone— an IANA zone name such asAfrica/Johannesburg, so the agent's clock and its answers about times use your user's zone. Omitted means UTC. A name that does not resolve is a400.
Submit and poll
# 1. hand it off
curl -X POST https://api.askamy.co.za/v1/agents/amy/runs/submit \
-H "X-Api-Key: $AMY_API_KEY" -H "Content-Type: application/json" \
-d '{"message": "Reconcile last month'"'"'s ledger and summarise the exceptions."}'
# → 202 {"runId":"01936c2f-…","status":"pending"}
# 2. read it back whenever
curl https://api.askamy.co.za/v1/runs/01936c2f-… -H "X-Api-Key: $AMY_API_KEY"GET /v1/runs/{runId} returns the whole persisted run — status, the messages
exchanged, token usage, any attachments it produced, and any approval it is waiting on. It is the
same view whether the run streamed, awaited or was submitted. A submitted run reads pending until it ends, including while it is being worked on, so poll for a final status.
The statuses a run can have:
- Still going:
pending,running. And paused, waiting on someone:awaiting_approval(decide it, see Approvals) andawaiting_work(the run is waiting on background tasks it started). - Finished well:
completed. - Stopped short, with what it had:
budget_exceeded,truncated,expired. These are policy stops, not errors. - Ended without an answer:
failed(readerrorCodeandfailureReason) andcancelled.
Every route keyed by a run id — this read, its file downloads, the approval resume, cancel and
retry — answers only the API client that started the run, with any of its keys. Another client's
run in the same tenant is a 404, exactly like an id that does not exist. One
exception: a team run's assignment approval. Team runs are a Console feature; if you do not use
them, ignore this. No client starts a team run, so an approval that belongs to one is not tied to
a client.
Cancel and retry
# stop a run
curl -X POST https://api.askamy.co.za/v1/runs/01936c2f-…/cancel -H "X-Api-Key: $AMY_API_KEY"
# → 200 {"cancelled":true,"status":"running"}
# re-drive a run that failed with a retryable code (streams, like a start)
curl -N -X POST https://api.askamy.co.za/v1/runs/01936c2f-…/retry -H "X-Api-Key: $AMY_API_KEY"POST /v1/runs/{runId}/cancel— stops a run. The answer is{ "cancelled", "status" }, andstatusis the status before the stop took effect, so poll the run forcancelled. A queued run is cancelled on the spot.cancelled: falseis not an error: the run had already ended, it is paused on an approval (decide that instead), it is waiting on its background tasks, or it is a task another run dispatched. Repeating a cancel is safe.POST /v1/runs/{runId}/retry— re-drives a run that endedfailedwith a retryable code, from the messages it already persisted. No tool runs again; the run is reopened (it goes back torunningand its failure fields clear) rather than copied. Its response streams exactly like a start. The body is optional; sendmcpAuthandmcpServersagain if the run used them. Checkretryableon the run first: it is true only for a retryable code, while the run is the latest turn of its conversation and its agent is active, and fewer thanretryCapretries are used (retriesUsedcounts them). Any refusal is a409retry_refusedwith areason, before the stream opens — see Errors. A retry is a new model call, so it passes the same plan, credential and pricing checks as a new run.
Reading a conversation back
Amy keeps the transcript, so your product does not have to. Both reads name one endUserRef, and return only runs your own API client started — with any of its keys,
so a rotated key reads what the old one wrote. A conversation id another client reused shows you
nothing of theirs.
# one user's conversations, most recently active first
curl "https://api.askamy.co.za/v1/conversations?endUserRef=your-own-user-id&page=1&pageSize=50" \
-H "X-Api-Key: $AMY_API_KEY"
# → {"items":[{"conversationId":"…","agentSlug":"amy","title":"…","status":"completed",
# "startedAt":"…","lastActivityAt":"…","runCount":3}],"page":1,"pageSize":50,"total":1}
# one conversation's turns, in order
curl "https://api.askamy.co.za/v1/conversations/…?endUserRef=your-own-user-id" -H "X-Api-Key: $AMY_API_KEY"
# → {…,"turns":[{"runId":"…","role":"user","text":"…","createdAt":"…"},
# {"runId":"…","role":"assistant","text":"…","createdAt":"…"}]}GET /v1/conversations— that user's conversations, most recently active first, paged withpage(from 1) andpageSize(default 50, at most 100). Each carries its title, its latest run'sstatus, when it started and was last active, and how many runs it holds.GET /v1/conversations/{conversationId}— the user and assistant turns, in order. A user turn is a run's message as the agent received it (a run that carried files ends with an[Attached files: …]line) or a note sent with an approval. Tool calls, their results and the messages Amy hands the agent itself, such as background-task results, are not part of it. A conversation that is not yours, or not this user's, is a404.
Runs that stop early
A run does not always finish on its own. It can pause for a human decision (see Approvals), stop because your token budget ran out (budget_exceeded), be cut short (truncated), or fail. The first two are policy stops carrying the partial answer, not errors. Read status rather than
inferring success from having received a terminal frame.