11. HTTP and AG-UI reference
11.1 Endpoints
| Method and path | Auth | Purpose |
|---|---|---|
GET /_ping, GET /_health, GET /_readiness | — | liveness; readiness also checks the database and queue |
GET /api/agents | — | the enabled workflows: id, title, description, version, ui, state_whitelist |
POST /agui/{workflow} | Bearer | start or resume a run (server-sent events) |
POST /api/documents | Bearer, documents.upload | upload; optional workflow field starts a run |
GET /api/runs/{thread} | Bearer, runs.read | status, whitelisted state and open interrupt |
A missing or invalid token is HTTP 401 before any stream opens.
11.2 AG-UI
Start a run. The request body is an AG-UI RunAgentInput:
{ "threadId": "t1", "runId": "r1", "protocolVersion": "1.0",
"messages": [{ "id": "m1", "role": "user", "content": "Invoice INV-1001 …" }],
"forwardedProps": { "document_ref": { "document_id": "<id from an upload>" } } }
- With no
document_ref, the last user message becomes the document. forwardedPropskeys become context values, except the engine's own (tenant_id,approval, and so on), which are refused.
Resume a run by addressing the open interrupt:
{ "threadId": "t1", "runId": "r2", "protocolVersion": "1.0",
"resume": [{ "interruptId": "t1:approval", "status": "resolved",
"payload": { "approved": true, "comment": "ok", "edits": { "invoice.po_number": "PO-7A" } } }] }
status: "cancelled" rejects. With resolved, payload.approved must be true or false.
The event stream. A resumed run, as received with curl -N:
data: {"type":"RUN_STARTED","threadId":"<thread>","runId":"r2","protocolVersion":"1.0"}
data: {"type":"STATE_SNAPSHOT","snapshot":{…}}
data: {"type":"TEXT_MESSAGE_START","messageId":"01a1c5a2-d494-…","role":"assistant"}
data: {"type":"TEXT_MESSAGE_CONTENT","messageId":"01a1c5a2-d494-…","delta":"Decision recorded"}
data: {"type":"TEXT_MESSAGE_END","messageId":"01a1c5a2-d494-…"}
data: {"type":"STATE_SNAPSHOT","snapshot":{…}}
data: {"type":"TEXT_MESSAGE_CONTENT","messageId":"…","delta":"accounting.post_bill done"}
data: {"type":"STATE_SNAPSHOT","snapshot":{…}}
data: {"type":"TEXT_MESSAGE_CONTENT","messageId":"…","delta":"Posted to accounting"}
data: {"type":"RUN_FINISHED","threadId":"<thread>","runId":"r2","result":{"end":"done"},"outcome":{"type":"success"}}
(Start and end events for the second and third messages are omitted.)
| Situation | Events |
|---|---|
| each step | STATE_SNAPSHOT (whitelisted state), then one narration message |
| run pauses (1.0 client) | MESSAGES_SNAPSHOT (the conversation), then RUN_FINISHED with outcome: { type: "interrupt", interrupts: [{ id, reason, message, responseSchema, expiresAt, metadata: { workflow, ui, roles, runId } }] } (runId is the request's, so every offer, including a re-offer, is distinguishable) |
| run pauses (pre-1.0 client) | MESSAGES_SNAPSHOT, CUSTOM named on_interrupt with { id, message, metadata }, then RUN_FINISHED with no outcome |
| decision refused while open | a Not accepted: … message, then the same interrupt again |
| run ends | RUN_FINISHED with outcome: { type: "success" } and result.end = the final node |
| error | RUN_ERROR with message and code: not_found, forbidden, bad_request, conflict or internal |
A client counts as 1.0 when protocolVersion has a major version of 1 or more.