API reference
One origin, one namespace
Everything lives under https://asteroidbelt.ai/api/v1 with a Bearer token. Management is request/response; conversation is a 202 send plus one standing SSE stream.
The reference below is rendered from the machine-readable spec — the same schemas the routes validate with: /api/v1/openapi.json.
Authentication
Every request sends Authorization: Bearer ast_…. Tokens are minted in Settings → API tokens or by ast auth login — never by other tokens. Scopes are ordered read ⊂ write ⊂ admin; a token is personal (acts as you everywhere) or scoped to one universe. Pass X-Universe: <slug> to act inside a non-personal universe.
Two unauthenticated endpoints back the ast auth login browser handoff (the PKCE grant code is the authentication; the token secret is returned exactly once):
/api/v1/cli/token-exchange{ "code", "verifier" } — exchanges an approved grant for the token secret./api/v1/cli/poll?request=&verifier=Errors
Every non-2xx body is { "error": { "code", "message" } }:
| HTTP | code | When |
|---|---|---|
| 400 | invalid_request | malformed JSON / validation failure |
| 401 | invalid_token | missing, unknown, revoked, or expired token |
| 402 | insufficient_credits | the spend gate blocked the turn |
| 403 | insufficient_scope · universe_mismatch · forbidden | scope too low; X-Universe conflicts with a scoped token; agent has no API binding |
| 404 | not_found | unknown agent/universe (also masks unauthorized slugs) |
| 409 | conflict | duplicate slug |
| 422 | invalid_spec | valid JSON, invalid domain rules (e.g. llm not allowed for type) |
| 429 | rate_limited | 120 requests/min per token exceeded — honor Retry-After |
Identity
Caller identity, resolved universe, effective scopes
Response
200OK4xx{ "error": { "code", "message" } } — see the error table above.curl https://asteroidbelt.ai/api/v1/whoami \
-H "Authorization: Bearer ast_..."Universes
List universes
Response
200OK4xx{ "error": { "code", "message" } } — see the error table above.curl https://asteroidbelt.ai/api/v1/universes \
-H "Authorization: Bearer ast_..."Create a universe
Body · application/json
slugstringrequiredmax length 40 — pattern ^[a-z0-9][a-z0-9-]*[a-z0-9]$
namestringmax length 80
Response
201Created4xx{ "error": { "code", "message" } } — see the error table above.curl -X POST https://asteroidbelt.ai/api/v1/universes \
-H "Authorization: Bearer ast_..." \
-H "Content-Type: application/json" \
-d '{"slug":"acme","name":"Acme"}'Universe detail
Path parameters
slugstringrequiredThe universe slug
Response
200OK4xx{ "error": { "code", "message" } } — see the error table above.curl https://asteroidbelt.ai/api/v1/universes/my-agent \
-H "Authorization: Bearer ast_..."Agents
List agents in the resolved universe
Response
200OK4xx{ "error": { "code", "message" } } — see the error table above.curl https://asteroidbelt.ai/api/v1/agents \
-H "Authorization: Bearer ast_..."Create an agent from a declarative spec
Every field optional — an empty body creates the instant default.
Body · application/json
namestringmax length 60
descriptionstringmax length 500
agentTypeenum<string>"hermes" · "claude_code" · "codex" · "openclaw"
llmstringpromptstringmax length 100000
displayNamestringmax length 60
capabilitiesenum<string>[]"exa.search" · "exa.contents" · "tinyfish.fetch" · "tinyfish.browser" · "dataforseo.serp" · "gptimage.generate" · "rembg.remove-background" · "seedance.generate" · "mm.i2v" · "fish-audio.transcribe" — default: []
instructionsobject[]▸ Show child attributes▾ Hide child attributes
namestringrequiredpattern .*\.md$
contentstringdefault: ""
skillsobject[]▸ Show child attributes▾ Hide child attributes
namestringrequiredpattern ^[A-Za-z0-9_-]+$
urlstringrequiredsourceenum<string>required"registry" · "git" · "package" · "archive"
versionstringrequiredconfigobjectdefault: {}
secretRefsstring[]default: []
customInstallsobject[]▸ Show child attributes▾ Hide child attributes
commandstringrequireddescriptionstringtimeoutSecondsintegerdefault: 300
allowNetworkbooleandefault: true
customDataobject[]▸ Show child attributes▾ Hide child attributes
idstringrequirednamestringrequiredpattern ^[a-z0-9_]+$
kindenum<string>required"spreadsheet" · "doc" · "private_api" · "other"
scopeenum<string>"creator" · "user" — default: "creator"
descriptionstringuristringconfigobjectdefault: {}
vcpusintegermemoryMbintegerResponse
201Created4xx{ "error": { "code", "message" } } — see the error table above.curl -X POST https://asteroidbelt.ai/api/v1/agents \
-H "Authorization: Bearer ast_..." \
-H "Content-Type: application/json" \
-d '{"name":"research-agent","agentType":"hermes","llm":"deepseek-v4-pro","capabilities":["exa.search"]}'Agent detail
Path parameters
slugstringrequiredThe agent slug
Response
200OK4xx{ "error": { "code", "message" } } — see the error table above.curl https://asteroidbelt.ai/api/v1/agents/my-agent \
-H "Authorization: Bearer ast_..."Update displayName / capabilities
Path parameters
slugstringrequiredThe agent slug
Body · application/json
displayNamestringmax length 60
capabilitiesenum<string>[]"exa.search" · "exa.contents" · "tinyfish.fetch" · "tinyfish.browser" · "dataforseo.serp" · "gptimage.generate" · "rembg.remove-background" · "seedance.generate" · "mm.i2v" · "fish-audio.transcribe" — default: []
Response
200OK4xx{ "error": { "code", "message" } } — see the error table above.curl -X PATCH https://asteroidbelt.ai/api/v1/agents/my-agent \
-H "Authorization: Bearer ast_..." \
-H "Content-Type: application/json" \
-d '{"displayName":"Research Agent"}'Destroy the pod and delete the agent
Path parameters
slugstringrequiredThe agent slug
Response
200OK4xx{ "error": { "code", "message" } } — see the error table above.curl -X DELETE https://asteroidbelt.ai/api/v1/agents/my-agent \
-H "Authorization: Bearer ast_..."Save + hot-reload the user prompt layer
Path parameters
slugstringrequiredThe agent slug
Body · application/json
promptstringrequiredmax length 100000
Response
200OK4xx{ "error": { "code", "message" } } — see the error table above.curl -X PUT https://asteroidbelt.ai/api/v1/agents/my-agent/prompt \
-H "Authorization: Bearer ast_..." \
-H "Content-Type: application/json" \
-d '{"prompt":"You are a meticulous research agent…"}'Prompt version history
Path parameters
slugstringrequiredThe agent slug
Response
200OK4xx{ "error": { "code", "message" } } — see the error table above.curl https://asteroidbelt.ai/api/v1/agents/my-agent/prompt/versions \
-H "Authorization: Bearer ast_..."Declared secret keys + set/unset — never values
Path parameters
slugstringrequiredThe agent slug
Response
200OK4xx{ "error": { "code", "message" } } — see the error table above.curl https://asteroidbelt.ai/api/v1/agents/my-agent/secrets \
-H "Authorization: Bearer ast_..."Set a declared secret's value (write-only)
Path parameters
slugstringrequiredThe agent slug
keystringrequiredBody · application/json
valuestringrequiredmax length 10000
descriptionstringmax length 500
Response
200OK4xx{ "error": { "code", "message" } } — see the error table above.curl -X PUT https://asteroidbelt.ai/api/v1/agents/my-agent/secrets/NOTION_TOKEN \
-H "Authorization: Bearer ast_..." \
-H "Content-Type: application/json" \
-d '{"value":"secret_…"}'Unset a secret
Path parameters
slugstringrequiredThe agent slug
keystringrequiredResponse
200OK4xx{ "error": { "code", "message" } } — see the error table above.curl -X DELETE https://asteroidbelt.ai/api/v1/agents/my-agent/secrets/NOTION_TOKEN \
-H "Authorization: Bearer ast_..."Conversation
Chat history (ascending row id) — the stream's catch-up surface
Path parameters
slugstringrequiredThe agent slug
Query parameters
sinceintegerReturn rows with id greater than this
limitintegerResponse
200OK4xx{ "error": { "code", "message" } } — see the error table above.curl https://asteroidbelt.ai/api/v1/agents/my-agent/messages?since=&limit= \
-H "Authorization: Bearer ast_..."Send a turn — 202 { turnId }; output arrives on the stream
The conversation plane (edge-routed to the chat-proxy). Optional Idempotency-Key header dedupes retries.
Path parameters
slugstringrequiredThe agent slug
Body · application/json
partsobject[]required▸ Show child attributes▾ Hide child attributes
type: "text"object▸ Show child attributes▾ Hide child attributes
indexintegerrequiredtype"text"requiredtextstringdefault: ""
type: "reasoning"object▸ Show child attributes▾ Hide child attributes
indexintegerrequiredtype"reasoning"requiredtextstringdefault: ""
redactedbooleandefault: false
type: "tool_call"object▸ Show child attributes▾ Hide child attributes
indexintegerrequiredtype"tool_call"requiredcall_idstringrequiredtool_namestringrequiredargsobjectdefault: {}
args_partialobject▸ Show child attributes▾ Hide child attributes
option 1object▸ Show child attributes▾ Hide child attributes
option 2object▸ Show child attributes▾ Hide child attributes
type: "tool_result"object▸ Show child attributes▾ Hide child attributes
indexintegerrequiredtype"tool_result"requiredcall_idstringrequiredtool_namestringrequiredstatusenum<string>"ok" · "error" — default: "ok"
resultobjectdefault: {}
type: "file"object▸ Show child attributes▾ Hide child attributes
indexintegerrequiredtype"file"requirednamestringrequiredmedia_typestringrequireduriobject▸ Show child attributes▾ Hide child attributes
option 1object▸ Show child attributes▾ Hide child attributes
option 2object▸ Show child attributes▾ Hide child attributes
dataobject▸ Show child attributes▾ Hide child attributes
option 1object▸ Show child attributes▾ Hide child attributes
option 2object▸ Show child attributes▾ Hide child attributes
size_bytesobject▸ Show child attributes▾ Hide child attributes
option 1object▸ Show child attributes▾ Hide child attributes
option 2object▸ Show child attributes▾ Hide child attributes
type: "image"object▸ Show child attributes▾ Hide child attributes
indexintegerrequiredtype"image"requiredmedia_typestringrequireduriobject▸ Show child attributes▾ Hide child attributes
option 1object▸ Show child attributes▾ Hide child attributes
option 2object▸ Show child attributes▾ Hide child attributes
dataobject▸ Show child attributes▾ Hide child attributes
option 1object▸ Show child attributes▾ Hide child attributes
option 2object▸ Show child attributes▾ Hide child attributes
altobject▸ Show child attributes▾ Hide child attributes
option 1object▸ Show child attributes▾ Hide child attributes
option 2object▸ Show child attributes▾ Hide child attributes
widthobject▸ Show child attributes▾ Hide child attributes
option 1object▸ Show child attributes▾ Hide child attributes
option 2object▸ Show child attributes▾ Hide child attributes
heightobject▸ Show child attributes▾ Hide child attributes
option 1object▸ Show child attributes▾ Hide child attributes
option 2object▸ Show child attributes▾ Hide child attributes
type: "video"object▸ Show child attributes▾ Hide child attributes
indexintegerrequiredtype"video"requiredmedia_typestringrequireduriobject▸ Show child attributes▾ Hide child attributes
option 1object▸ Show child attributes▾ Hide child attributes
option 2object▸ Show child attributes▾ Hide child attributes
dataobject▸ Show child attributes▾ Hide child attributes
option 1object▸ Show child attributes▾ Hide child attributes
option 2object▸ Show child attributes▾ Hide child attributes
altobject▸ Show child attributes▾ Hide child attributes
option 1object▸ Show child attributes▾ Hide child attributes
option 2object▸ Show child attributes▾ Hide child attributes
widthobject▸ Show child attributes▾ Hide child attributes
option 1object▸ Show child attributes▾ Hide child attributes
option 2object▸ Show child attributes▾ Hide child attributes
heightobject▸ Show child attributes▾ Hide child attributes
option 1object▸ Show child attributes▾ Hide child attributes
option 2object▸ Show child attributes▾ Hide child attributes
duration_msobject▸ Show child attributes▾ Hide child attributes
option 1object▸ Show child attributes▾ Hide child attributes
option 2object▸ Show child attributes▾ Hide child attributes
size_bytesobject▸ Show child attributes▾ Hide child attributes
option 1object▸ Show child attributes▾ Hide child attributes
option 2object▸ Show child attributes▾ Hide child attributes
thumbnail_uriobject▸ Show child attributes▾ Hide child attributes
option 1object▸ Show child attributes▾ Hide child attributes
option 2object▸ Show child attributes▾ Hide child attributes
type: "status"object▸ Show child attributes▾ Hide child attributes
indexintegerrequiredtype"status"requiredlevelenum<string>required"thinking" · "working" · "waiting" · "idle" · "done"
textobject▸ Show child attributes▾ Hide child attributes
option 1object▸ Show child attributes▾ Hide child attributes
option 2object▸ Show child attributes▾ Hide child attributes
type: "error"object▸ Show child attributes▾ Hide child attributes
indexintegerrequiredtype"error"requiredcodestringrequiredmessagestringrequiredretryablebooleandefault: false
Response
202Accepted — { turnId }4xx{ "error": { "code", "message" } } — see the error table above.curl -X POST https://asteroidbelt.ai/api/v1/agents/my-agent/messages \
-H "Authorization: Bearer ast_..." \
-H "Content-Type: application/json" \
-d '{"parts":[{"type":"text","index":0,"text":"Summarize today's signups"}]}'{
"turnId": "turn_8c41f0…"
}The standing event stream (SSE)
text/event-stream, live-only, stays open across turns. Events: user.message, turn.accepted, agent.message, agent.reasoning, agent.tool_use, agent.tool_result, agent.status, turn.status_idle, turn.interrupted, error. Reconnect = reopen + list history + dedup by event id.
Path parameters
slugstringrequiredThe agent slug
Response
200SSE stream4xx{ "error": { "code", "message" } } — see the error table above.curl -N https://asteroidbelt.ai/api/v1/agents/my-agent/stream \
-H "Authorization: Bearer ast_..."Abort the in-flight turn
Path parameters
slugstringrequiredThe agent slug
Response
200{ turnId, status: "interrupting" } or { turnId: null, status: "no_turn" }4xx{ "error": { "code", "message" } } — see the error table above.curl -X POST https://asteroidbelt.ai/api/v1/agents/my-agent/interrupt \
-H "Authorization: Bearer ast_..."{
"turnId": "turn_8c41f0…",
"status": "interrupting"
}Logs
Pod logs; follow=true streams as SSE
Path parameters
slugstringrequiredThe agent slug
Query parameters
sincestringe.g. "30m", "2h", "1d"
followbooleanResponse
200OK4xx{ "error": { "code", "message" } } — see the error table above.curl https://asteroidbelt.ai/api/v1/agents/my-agent/logs?since=&follow= \
-H "Authorization: Bearer ast_..."Stream events
Send and listen are decoupled: POST …/messages, then read …/stream until turn.status_idle — there is no non-streaming reply mode. Frames are id: + event: + one data: JSON envelope { type, turnId?, agent, at, …payload }:
| event | payload | meaning |
|---|---|---|
user.message | parts, text, channel | input echo — a turn arrived, from this API or any channel |
turn.accepted | — | turn validated, queued to the agent |
agent.message | parts, text | a user-facing agent post; a turn may carry several |
agent.reasoning | part | raw engine event: thinking |
agent.tool_use | part | raw engine event: a tool invocation |
agent.tool_result | part | raw engine event: the tool's outcome |
agent.status | part | raw engine event: working / waiting / … |
turn.status_idle | — | turn complete; the stream stays open |
turn.interrupted | by | an interrupt took effect |
error | code, message, retryable | the turn failed; the stream stays open |
event: user.message
data: {"type":"user.message","turnId":"turn_8c41f0…","agent":"nemesis-ee87","at":"…","channel":"api","text":"Summarize today's signups.","parts":[…]}
event: turn.accepted
data: {"type":"turn.accepted","turnId":"turn_8c41f0…","agent":"nemesis-ee87","at":"…"}
event: agent.message
data: {"type":"agent.message","turnId":"turn_8c41f0…","agent":"nemesis-ee87","at":"…","text":"Signups today: 42, up 12%.","parts":[…]}
event: turn.status_idle
data: {"type":"turn.status_idle","turnId":"turn_8c41f0…","agent":"nemesis-ee87","at":"…"}