REST API
The engine serves four HTTP surfaces on one host. Requests and responses are
JSON; streaming endpoints return text/event-stream. Every shape used in a body
is defined under Objects, so this page stands on its own.
| Surface | Prefix | Auth | For |
|---|---|---|---|
| Client | /api/client | Client JWT | End-user clients (browser, app backend). |
| Machine | /api/machine | API key | Your backend and worker callbacks. |
| Admin | /api/admin | API key | Self-hosted session inspection. |
| Cloud | /api/v1 | API key | The hosted control plane the subs cloud commands wrap. |
Base URL
Prepend your engine's host to every path.
- Self-hosted: your
subs serveaddress, such ashttp://localhost:8080. All four surfaces share it. - Cloud: the control plane (
/api/v1) ishttps://api.substructure.ai. The client and machine surfaces are served at your app's own engine URL, shown bysubs openand handed to clients alongside their token.
Headers
| Header | When |
|---|---|
Authorization: Bearer <token> | Every request. Which token depends on the surface (see each section). |
Content-Type: application/json | Any request with a body. |
Accept: text/event-stream | The streaming and AG-UI endpoints. |
Errors
Failures return a status and a JSON body. Most surfaces use the first shape;
the machine submit and settle endpoints use the second.
{ "error": "session not found" }{ "ok": false, "error": "effect is not pending" }| Status | Meaning |
|---|---|
| 202 | Input accepted. |
| 400 | Malformed request or a validation failure. |
| 401 | Missing or invalid credential. |
| 403 | Wrong tenant, not the session owner, or wrong caller for the effect. |
| 404 | Unknown session or effect. |
| 409 | Conflict: session interrupted, effect not pending, or a completed turn. |
| 422 | Unrecognized JSON body. |
Streaming
The .../events/stream endpoints return text/event-stream. Each frame carries
id (the event's stream version), event (the event type), and data (an
Event envelope). Resume after a drop with
?after_stream_version=<n>; the id of the last frame you saw is that number.
id: 7
event: message.new
data: {"global_position":42,"aggregate_id":"0193a1","stream_version":7,"occurred_at":"2026-07-14T10:00:03Z","payload":{"type":"message.new","message":{"id":"m2","role":"assistant","content":"Hi"}}}
event: llm.token.delta
data: {"type":"llm.token.delta","session_id":"0193a1","call_id":"llm-1","seq":3,"text":"Hi"}llm.token.delta frames stream partial assistant output between events; see
StreamDelta. The event value is one of:
| Event | Payload highlights |
|---|---|
message.new | A message was added to the tree: { message: Message, parent_id? }. |
turn.started / turn.completed | Turn boundaries, { turn_id, … }; turn.completed closes a turn-scoped stream. |
tool.call.requested / .completed / .errored | { tool_call_id, name, result? / error? }. |
llm.call.requested / .completed / .errored | An LLM call's lifecycle. |
sub_agent.requested / .started / .turn_completed / .errored | A child session's lifecycle. |
session.created / .done / .cancelled | Session lifecycle. |
session.interrupted / .interrupt_resumed | { interrupt_id, origin, reason, payload, anchor? }; anchor is the head the interrupt parks (absent = every branch). |
worker.state.updated / agent.updated | State or config was rewritten. |
worker.decision.requested / .completed / .errored, decision_request.queued / .dropped, call.voided, session.message_requested | Engine bookkeeping, useful for observability. |
The AG-UI endpoints emit AG-UI protocol events instead; see AG-UI events.
Client API
For end-user clients: a browser, or your app's frontend.
Authentication. Authorization: Bearer <client-jwt>. A client token is
short-lived and minted by your backend through the machine API's
client-tokens endpoint; never ship an API key to a browser. The token fixes
the tenant and owner, so a client acts only as itself. This surface answers CORS
preflight, so it is safe to call from a browser.
curl $BASE/api/client/sessions/input \
-H "Authorization: Bearer $CLIENT_JWT" \
-H "Content-Type: application/json" \
-d '{"input":{"type":"client.message","agent_id":"my-agent","message":{"role":"user","content":"hi"}}}'POST /api/client/sessions/input
Submit a ClientInput: a message, action, interrupt resume, or
client-tool settle. session_id is minted when omitted; a completed turn_id
returns the existing turn instead of re-running it.
Headers Authorization: Bearer <client-jwt>, Content-Type: application/json
Request body
{
"session_id": "0193a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b",
"input": {
"type": "client.message",
"agent_id": "my-agent",
"message": { "role": "user", "content": "what time is it?" }
}
}Response 202
{
"session_id": "0193a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b",
"turn_id": "0193a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a60"
}GET /api/client/sessions/{session_id}
The session resource: head-resolved status, every open interrupt, messages —
the active conversation (root→head lineage), ready to render — and the full
message tree for clients that show branches. Enough
for a client to rehydrate a conversation on page load.
Headers Authorization: Bearer <client-jwt>
Path parameters
| Parameter | Description |
|---|---|
session_id | The session to fetch. |
Response 200
{
"session_id": "sess-1",
"agent_id": "my-agent",
"status": "idle",
"interrupts": [],
"messages": [
{ "id": "m1", "role": "user", "content": "hi" },
{ "id": "m2", "role": "assistant", "content": "Hello!" }
],
"message_tree": {
"head_id": "m2",
"nodes": [
{ "message": { "id": "m1", "role": "user", "content": "hi" } },
{ "parent_id": "m1", "message": { "id": "m2", "role": "assistant", "content": "Hello!" } }
]
}
}status is idle, interrupted, or done, resolved at the active branch: it
reads interrupted only when an open interrupt parks the head path.
interrupts lists every open interrupt on any branch as
{ interrupt_id, origin, reason, anchor? } — anchor is the message the
interrupt is parked at (absent = parks every branch). A session that has never
received input is 404.
GET /api/client/sessions/{session_id}/events/stream
Stream a session's events.
Headers Authorization: Bearer <client-jwt>, Accept: text/event-stream
Path parameters
| Parameter | Description |
|---|---|
session_id | The session to stream. |
Query parameters
| Parameter | Type | Description |
|---|---|---|
turn_id | string | Optional. Scope to one turn; the stream closes when it completes. |
after_stream_version | integer | Optional. Resume after this stream version. |
Response text/event-stream. See Streaming.
POST /api/client/sessions/{session_id}/interrupt
Pause the session's active branch. The interrupt anchors at the current head;
other branches stay live (see Interrupts).
interrupt_id is minted when omitted.
Headers Authorization: Bearer <client-jwt>, Content-Type: application/json
Path parameters
| Parameter | Description |
|---|---|
session_id | The session to interrupt. |
Request body
{
"reason": "confirm",
"payload": { "message": "Send the email?" }
}Response 200
{ "ok": true, "interrupt_id": "int-1" }POST /api/client/ag-ui/agents/{agent_id}/run
Run a turn from an AG-UI client.
Headers Authorization: Bearer <client-jwt>, Content-Type: application/json, Accept: text/event-stream
Path parameters
| Parameter | Description |
|---|---|
agent_id | The agent to run. |
Request body: a RunAgentInput.
{
"threadId": "sess-1",
"runId": "turn-1",
"messages": [{ "id": "m1", "role": "user", "content": "hi" }],
"tools": [],
"context": [],
"state": {},
"resume": []
}Response text/event-stream of AG-UI events.
event: RUN_STARTED
data: {"type":"RUN_STARTED","threadId":"sess-1","runId":"turn-1"}
event: TEXT_MESSAGE_CONTENT
data: {"type":"TEXT_MESSAGE_CONTENT","messageId":"m2","delta":"Hi"}
event: RUN_FINISHED
data: {"type":"RUN_FINISHED","threadId":"sess-1","runId":"turn-1"}POST /api/client/ag-ui/agents/{agent_id}/connect
Replay the session as a one-shot AG-UI snapshot, for reconnecting.
Headers Authorization: Bearer <client-jwt>, Content-Type: application/json, Accept: text/event-stream
Path parameters
| Parameter | Description |
|---|---|
agent_id | Present for symmetry; only threadId in the body is used. |
Request body: threadId addresses the session; runId is optional and
labels the snapshot frames.
{ "threadId": "sess-1" }Response text/event-stream: RUN_STARTED, MESSAGES_SNAPSHOT,
RUN_FINISHED, then close.
Machine API
For your own backend and for worker callbacks. Server-side only.
Authentication. Authorization: Bearer <api-key>, the app's API key. It
carries full tenant privileges, so keep it on your server and never expose it to
a browser. This surface has no CORS. It is where you mint client tokens, submit
on a user's behalf, and settle deferred calls.
curl $BASE/api/machine/client-tokens \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{"identity":{"id":"user_42"}}'POST /api/machine/client-tokens
Mint a client JWT for a browser session.
Headers Authorization: Bearer <api-key>, Content-Type: application/json
Request body. identity.id is required; ttl_seconds defaults to 600.
{
"identity": { "id": "user_42", "metadata": { "plan": "pro" } },
"ttl_seconds": 600
}Response 200. expires_at is Unix seconds.
{ "token": "eyJhbGciOiJIUzI1NiJ9...", "expires_at": 1784000000 }POST /api/machine/sessions/submit
Submit a ClientPayload from your backend; the owner is
attested from your key and the request identity.
Headers Authorization: Bearer <api-key>, Content-Type: application/json
Request body. agent_id and identity.id are required.
{
"agent_id": "my-agent",
"identity": { "id": "user_42" },
"session_id": "0193a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b",
"payload": {
"type": "client.message",
"message": { "role": "user", "content": "hi" }
}
}Response 202
{
"session_id": "0193a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b",
"turn_id": "0193a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a60"
}POST /api/machine/sessions/{session_id}/calls/settle
Settle a worker-handled tool or LLM call out of band, by effect id. This is
how a deferred call reports its result later.
Headers Authorization: Bearer <api-key>, Content-Type: application/json
Path parameters
| Parameter | Description |
|---|---|
session_id | The session the call belongs to. |
Request body. One of four variants (response is an
LlmResponse, code an ErrorCode):
{ "type": "tool.result", "id": "call_abc", "result": "sunny" }
{ "type": "tool.error", "id": "call_abc", "error": "upstream 503", "retryable": true, "code": "provider_error" }
{ "type": "llm.result", "id": "llm_abc", "response": { "model": "gpt-4", "content": "…" } }
{ "type": "llm.error", "id": "llm_abc", "error": "rate limited", "retryable": true }Response 200
{ "ok": true }POST /api/machine/workers/submit
Push a worker-authored DecisionResponse into the engine
out of band, keyed by session_id and decision_id. Every settle action must
name its id and every llm.call its model, since no trigger is in scope to
fill them.
Headers Authorization: Bearer <api-key>, Content-Type: application/json
Request body
{
"session_id": "0193a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b",
"decision_id": "dec_abc",
"actions": [{ "type": "tool.result", "id": "call_abc", "result": "sunny" }]
}Response 200
{ "ok": true }GET /api/machine/sessions/{session_id}/events/stream
The session event stream under API-key auth.
Headers Authorization: Bearer <api-key>, Accept: text/event-stream
Path parameters
| Parameter | Description |
|---|---|
session_id | The session to stream. |
Query parameters
| Parameter | Type | Description |
|---|---|---|
turn_id | string | Optional. Scope to one turn. |
after_stream_version | integer | Optional. Resume after this stream version. |
Response text/event-stream. See Streaming.
Admin API
Read-only session inspection on a self-hosted server.
Authentication. Authorization: Bearer <api-key>, the same key as the
machine surface. Results are scoped to the key's tenant. These routes exist only
on a subs serve host, not in the cloud.
curl $BASE/api/admin/sessions -H "Authorization: Bearer $API_KEY"GET /api/admin/sessions
List sessions, newest first.
Headers Authorization: Bearer <api-key>
Query parameters
| Parameter | Type | Description |
|---|---|---|
top_level | boolean | Default true. Exclude sub-agent sessions. |
sort | string | last_event_desc (default), first_event_asc, first_event_desc, or wake_at_asc. |
limit | integer | Page size. |
cursor | string | Page cursor; echo next_cursor from the previous page. |
session_id | string | Filter to one session. |
agent_id | string | Filter by agent. |
Response 200
{
"items": [
{
"session_id": "0193a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b",
"tenant_id": "default",
"stream_version": 12,
"first_event_at": "2026-07-14T10:00:00Z",
"last_event_at": "2026-07-14T10:03:00Z",
"wake_at": null,
"top_level": true,
"agent_id": "my-agent",
"cost": "0.0012",
"sub_agent_cost": "0",
"status": "idle",
"turn_id": null
}
],
"next_cursor": null
}GET /api/admin/sessions/{session_id}
Fetch one session's current state. state is the internal session aggregate
(owner, status, running cost, message tree, open calls). Treat it as read-only
diagnostics; it may grow fields.
Headers Authorization: Bearer <api-key>
Path parameters
| Parameter | Description |
|---|---|
session_id | The session. |
Response 200
{
"stream_version": 12,
"first_event_at": "2026-07-14T10:00:00Z",
"last_event_at": "2026-07-14T10:03:00Z",
"state": {
"status": "idle",
"owner": { "tenant_id": "default", "id": "user_42" },
"cost": "0.0012",
"message_tree": { "nodes": [], "head_id": "m2" }
}
}GET /api/admin/sessions/{session_id}/events
Fetch stored Events.
Headers Authorization: Bearer <api-key>
Path parameters
| Parameter | Description |
|---|---|
session_id | The session. |
Query parameters
| Parameter | Type | Description |
|---|---|---|
after_stream_version | integer | Return events after this version. |
limit | integer | Page size. |
Response 200
[
{
"global_position": 128,
"id": "0193a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a70",
"tenant_id": "default",
"aggregate_type": "session",
"aggregate_id": "0193a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b",
"stream_version": 3,
"occurred_at": "2026-07-14T10:00:03Z",
"payload": {
"type": "message.new",
"message": { "id": "m2", "role": "assistant", "content": "Hi" }
}
}
]GET /api/admin/sessions/{session_id}/events/stream
The same events as SSE. after_stream_version defaults to 0, replaying full
history.
Headers Authorization: Bearer <api-key>, Accept: text/event-stream
Path parameters
| Parameter | Description |
|---|---|
session_id | The session to stream. |
Query parameters
| Parameter | Type | Description |
|---|---|---|
after_stream_version | integer | Default 0. Resume after this version. |
limit | integer | Optional cap on replayed history. |
Response text/event-stream. See Streaming.
POST /api/admin/sessions/{session_id}/ag-ui/connect
Replay the session as an AG-UI snapshot.
Headers Authorization: Bearer <api-key>, Content-Type: application/json, Accept: text/event-stream
Path parameters
| Parameter | Description |
|---|---|
session_id | The session to replay. |
Request body: a RunAgentInput.
Response text/event-stream of AG-UI events.
Cloud API
The hosted control plane at https://api.substructure.ai, wrapped by the subs
cloud commands (see CLI).
Authentication. Authorization: Bearer <token>: your subs login token
against the cloud, or an app API key against a local server. A local subs serve
re-serves the same shape as a single tenant, so the {org} and {app} segments
are ignored, and the mutating endpoints return 400:
{ "error": { "code": "unsupported_on_local", "message": "this operation is not supported on a local server; it only exists in the hosted cloud" } }curl https://api.substructure.ai/api/v1/orgs -H "Authorization: Bearer $TOKEN"GET /api/v1/orgs
List the orgs you belong to.
Headers Authorization: Bearer <token>
Response 200
[{ "id": "org_1", "name": "Acme", "role": "owner" }]GET /api/v1/orgs/{org}/apps
List an org's apps. Returns an array of App.
Headers Authorization: Bearer <token>
Path parameters
| Parameter | Description |
|---|---|
org | The org id. |
GET /api/v1/apps/{app}
Fetch one app.
Headers Authorization: Bearer <token>
Path parameters
| Parameter | Description |
|---|---|
app | The app id. |
Response 200. An App. balanceUsd and sessionCount are
omitted on a local server.
{
"id": "app_abc",
"organizationId": "org_1",
"name": "my-bot",
"createdAt": "2026-07-01T00:00:00Z",
"balanceUsd": "42.50",
"sessionCount": 1280
}POST /api/v1/orgs/{org}/apps
Create an app. Hosted only.
Headers Authorization: Bearer <token>, Content-Type: application/json
Path parameters
| Parameter | Description |
|---|---|
org | The org to create the app in. |
Request body
{ "name": "my-bot" }Response 200
{
"app": { "id": "app_abc", "organizationId": "org_1", "name": "my-bot" },
"signing_secret": "9f3c1a…"
}PATCH /api/v1/apps/{app}
Rename an app. Hosted only.
Headers Authorization: Bearer <token>, Content-Type: application/json
Path parameters
| Parameter | Description |
|---|---|
app | The app id. |
Request body: { "name": "new-name" }. Response 200: the updated
App.
DELETE /api/v1/apps/{app}
Delete an app. Hosted only.
Headers Authorization: Bearer <token>
Path parameters
| Parameter | Description |
|---|---|
app | The app id. |
GET /api/v1/apps/{app}/worker
Read the webhook worker.
Headers Authorization: Bearer <token>
Path parameters
| Parameter | Description |
|---|---|
app | The app id. |
Response 200
{
"endpoint_url": "https://my-worker.example.com/agent",
"state": "enabled",
"signing_secret": "9f3c1a…"
}PUT /api/v1/apps/{app}/worker
Set the webhook worker. Enabling reuses or mints the signing secret.
Headers Authorization: Bearer <token>, Content-Type: application/json
Path parameters
| Parameter | Description |
|---|---|
app | The app id. |
Request body. state is "enabled" or "disabled".
{ "endpointUrl": "https://my-worker.example.com/agent", "state": "enabled" }Response 200. The worker config, as GET returns.
POST /api/v1/apps/{app}/worker/rotate-secret
Rotate the signing secret. No body; returns the worker config with the new secret.
Headers Authorization: Bearer <token>
Path parameters
| Parameter | Description |
|---|---|
app | The app id. |
GET /api/v1/apps/{app}/api-keys
List client API keys. Hosted only.
Headers Authorization: Bearer <token>
Response 200
[{ "key_id": "key_1", "label": "web-frontend", "created_at": "…", "last_used_at": null }]POST /api/v1/apps/{app}/api-keys
Create a client API key. Hosted only.
Headers Authorization: Bearer <token>, Content-Type: application/json
Request body: { "label": "web-frontend" }.
Response 200. The key is shown once.
{ "api_key": "sk_live_…" }DELETE /api/v1/apps/{app}/api-keys/{key_id}
Revoke a key. Hosted only.
Headers Authorization: Bearer <token>
Path parameters
| Parameter | Description |
|---|---|
app | The app id. |
key_id | The key to revoke. |
GET /api/v1/apps/{app}/sessions…
Session reads, identical to the Admin API with the {app} segment
ignored on a local server: /sessions, /sessions/{id}, /sessions/{id}/events,
and /sessions/{id}/events/stream.
Objects
The types used in the bodies above. ? marks an optional field; unknown is
any JSON value. These mirror the machine-readable
schemas/protocol.schema.json; see
Typed bindings to generate them.
Messages
type Role = "system" | "user" | "assistant" | "tool"
type Content = string | ContentPart[]
type ContentPart =
| { type: "text"; text: string }
| { type: "image_url"; image_url: { url: string } }
| { type: "file"; file: { filename: string; file_data: string } }
| { type: "input_audio"; input_audio: { data: string; format: string } }
| { type: "video_url"; video_url: { url: string } }
type ToolCall = { id: string; type: string; function: { name: string; arguments: string } }
// A message; the id is optional when you submit one (the engine assigns it).
type Message = {
id?: string
role: Role
content?: Content
tool_calls?: ToolCall[]
tool_call_id?: string
name?: string
}Client input
The body of POST /api/client/sessions/input is { session_id?, input: ClientInput }.
type ClientInput =
| { type: "client.message"; agent_id: string; turn_id?: string; message: Message; stream?: boolean }
| { type: "client.messages"; agent_id: string; turn_id?: string; messages: Message[]; stream?: boolean; client?: ClientContext }
| { type: "client.action"; agent_id: string; turn_id?: string; name: string; args?: unknown }
| { type: "interrupt.resume"; interrupt_id: string; payload?: unknown }
| { type: "tool.result"; id: string; attempt?: number; result?: unknown }
| { type: "tool.error"; id: string; error: string; retryable: boolean; attempt?: number }
// Browser-contributed tools and context, layered onto the agent for this turn.
type ClientContext = {
tools?: AgentTool[]
context?: unknown[]
state?: unknown
forwarded_props?: unknown
}
type AgentTool = {
name: string
description?: string
input?: unknown // JSON Schema for arguments
output?: unknown // JSON Schema the result must satisfy
handler?: "worker" | "client"
}turn_id is an idempotency key; resubmitting a completed one returns the
existing turn.
Client payload
The payload of POST /api/machine/sessions/submit: a client input without the
addressing fields, which the machine supplies separately.
type ClientPayload =
| { type: "client.message"; message: Message; stream?: boolean }
| { type: "client.messages"; messages: Message[]; stream?: boolean; client?: ClientContext }
| { type: "client.action"; name: string; args?: unknown }Decision response
The body of POST /api/machine/workers/submit.
type DecisionResponse = {
messages?: Message[] // messages to record
actions?: Action[] // what the engine should do next
state?: unknown // omitted or null keeps current state
agent?: AgentConfig // omitted keeps current config
}
type Action =
| { type: "llm.call"; id?: string; model?: string; messages?: Message[]; tools?: LlmTool[]; temperature?: number; max_completion_tokens?: number; reasoning?: ReasoningConfig; stream?: boolean; retry?: RetryPolicy; handler?: "server" | "worker" }
| { type: "tool.call"; id?: string; name: string; arguments: unknown; handler?: "worker" | "client"; retry?: RetryPolicy }
| { type: "tool.result"; id?: string; attempt?: number; result: unknown }
| { type: "tool.error"; id?: string; attempt?: number; error: string; retryable?: boolean; code?: ErrorCode; detail?: unknown }
| { type: "llm.result"; id?: string; attempt?: number; response: LlmResponse }
| { type: "llm.error"; id?: string; attempt?: number; error: string; retryable?: boolean; code?: ErrorCode; detail?: unknown }
| { type: "sub_agent.spawn"; session_id: string; agent_id: string; tool_call_id: string; retry?: RetryPolicy }
| { type: "message.send"; session_id: string; message: Message }
| { type: "interrupt"; interrupt_id?: string; reason: string; payload?: unknown }
| { type: "done"; data?: unknown }
type AgentConfig = {
model: string
system?: string
stream?: boolean
handler?: "server" | "worker"
format?: "openai" | "anthropic"
retry?: RetryPolicy
tools?: AgentTool[]
sub_agents?: { id: string; description?: string }[]
}
type LlmTool = { name: string; description: string; input?: unknown; output?: unknown }
type ReasoningConfig = { effort?: "xhigh" | "high" | "medium" | "low" | "minimal" | "none"; max_tokens?: number; exclude?: boolean; enabled?: boolean }LLM response
Used in the llm.result settle and action.
type LlmResponse = {
model: string
content?: string
tool_calls?: ToolCall[]
finish_reason?: string
usage?: unknown
cost?: string // dollars, decimal string
images?: { url: string }[]
}Retries and errors
type RetryPolicy = {
timeout_secs: number | null // deadline per attempt; null waits forever
max_retries: number // cap on total attempts
backoff_base_secs: number
backoff_max_secs: number
}
type ErrorCode = "provider_error" | "rate_limited" | "refused" | "budget_exceeded" | "deadline_exceeded"Events
A stored event, from the event-stream and .../events endpoints. payload is
tagged by type (see the Streaming table).
type Event = {
global_position: number
id: string
tenant_id: string
aggregate_type: string // "session"
aggregate_id: string // the session id
stream_version: number // the SSE frame id
occurred_at: string // RFC 3339
payload: { type: string; [field: string]: unknown }
}
type StreamDelta = { // the llm.token.delta data
type: "llm.token.delta"
session_id: string
call_id: string
seq: number
text?: string
reasoning?: string
tool_calls?: { id: string; name?: string; arguments?: string }[]
finish_reason?: string
}App
The Cloud App resource. balanceUsd and sessionCount are omitted on a local
server.
type App = {
id: string
organizationId: string
name: string
createdAt?: string
balanceUsd?: string // decimal string
sessionCount?: number
}RunAgentInput
The AG-UI request body (camelCase).
type RunAgentInput = {
threadId: string // the session id
runId: string // the turn id
messages: { id?: string; role: string; content?: string; toolCallId?: string; toolCalls?: unknown[] }[]
state?: unknown
tools?: { name: string; description?: string; parameters?: unknown }[]
context?: unknown[]
forwardedProps?: unknown
resume?: { interruptId: string; status: "resolved" | "cancelled"; payload?: unknown }[]
}AG-UI events
The AG-UI run and connect streams emit these, each a frame whose event is
the type and data is a camelCase JSON object.
RUN_STARTED, RUN_FINISHED, RUN_ERROR, MESSAGES_SNAPSHOT,
TEXT_MESSAGE_START, TEXT_MESSAGE_CONTENT, TEXT_MESSAGE_END,
TOOL_CALL_START, TOOL_CALL_ARGS, TOOL_CALL_END, TOOL_CALL_RESULT,
REASONING_START, REASONING_MESSAGE_START, REASONING_MESSAGE_CONTENT,
REASONING_MESSAGE_END, REASONING_END. RUN_FINISHED carries an interrupt
outcome when the agent paused. See AG-UI.
Next
- Authentication: tokens, keys, and identity.
- CLI: the commands that drive the cloud API.
- Typed bindings: generate these objects in your language.