Skip to content

HTTP API

The awaken-server crate (feature flag server) exposes an HTTP API via Axum. Most responses are JSON. Streaming endpoints use Server-Sent Events (SSE).

This page mirrors the current route tree in crates/awaken-server/src/routes.rs, config_routes.rs, event_routes.rs, eval_router.rs, system_routes.rs, and the protocol modules.

Admin, config, eval, trace, and system routes require Authorization: Bearer <token>. The token comes from AdminApiConfig.bearer_token or AWAKEN_ADMIN_API_BEARER_TOKEN. If a route is mounted but no admin token is configured, it returns 401 instead of falling back to anonymous access.

Server startup also validates the exposed admin surface. If config, eval, or trace routes are exposed, build_service_router refuses to start without an admin token. Protocol run routes are separate from the admin control plane and should be protected by the embedder’s deployment boundary.

SurfaceMounted whenAuth/scope
Health, threads, runsAlways present on ServerStateAgent-invocation scope except health
Protocol routes (AI SDK, AG-UI, A2A, MCP HTTP)Always present on ServerStateAgent-invocation scope
Admin assistant streamProtocol module plus config module wiringAdmin bearer + admin scope
Canonical event routesEventModuleState is wiredEvent-store availability controls behavior
System routesAlways mountedAdmin bearer + admin scope
Config and capabilitiesAdminApiConfig.expose_config_routes and ConfigStore wiringAdmin bearer + admin scope
Admin run summary/runtime statsAdminApiConfig.expose_config_routesAdmin bearer + admin scope
Eval routesAdminApiConfig.expose_eval_routes plus config/eval module wiringAdmin bearer + admin scope
Trace routesAdminApiConfig.expose_trace_routes plus trace module wiringAdmin bearer + admin scope
MetricsAlways mountedDeployment boundary
MethodPathDescription
GET/healthReadiness probe. Checks store connectivity and returns 200 or 503
GET/health/liveLiveness probe. Always returns 200 OK
GET/v1/system/infoServer identity for the admin console: {version, scope_id, uptime_seconds, config_store_enabled, audit_log_enabled, runtime_stats_enabled}
GET/v1/system/modulesMounted module names, for example ["run","admin","protocol","config","events","eval"]
GET/metricsPrometheus scrape endpoint

GET /v1/system/info is the admin-console “System” card source. scope_id is the trusted HttpScopeProvider result for the current admin request and is displayed read-only; it is not accepted as a query/body parameter. The route does not reveal concrete store backends — embedders that want to expose those should add a separate route on top of their own ServerState.

MethodPathDescription
GET/v1/threadsList thread IDs with paging and lineage filters; returns { items, offset, limit, total, has_more, next_cursor }
POST/v1/threadsCreate a thread. Body: { "title"?: string, "resource_id"?: string, "parent_thread_id"?: string }
GET/v1/threads/summariesList thread summaries (id, resource_id, parent_thread_id, title, updated_at, agent_id) with the same paging and lineage filters as /v1/threads
GET/v1/threads/:idGet a thread by ID
PATCH/v1/threads/:idUpdate thread metadata
DELETE/v1/threads/:idDelete a thread; accepts ?child_strategy=detach|reject|cascade (default detach) to control how direct and transitive child threads are handled
POST/v1/threads/:id/cancelCancel a specific queued or running dispatch addressed by this thread ID. Returns cancel_requested.
POST/v1/threads/:id/decisionSubmit a HITL decision for a waiting run on this thread
POST/v1/threads/:id/interruptInterrupt the thread: bumps the thread dispatch epoch, supersedes all pending queued dispatches, and cancels the active run. Returns interrupt_requested with superseded_dispatches count. Unlike /cancel, this performs a clean-slate interrupt via mailbox.interrupt().
PATCH/v1/threads/:id/metadataAlias for thread metadata updates
GET/v1/threads/:id/messagesList thread messages with cursor pagination, sequence range, ordering, and visibility/run filters
POST/v1/threads/:id/messagesSubmit messages as a background run on this thread
POST/v1/threads/:id/mailboxPush a message payload to the thread mailbox
GET/v1/threads/:id/mailboxList mailbox dispatches for the thread
GET/v1/threads/:id/eventsList durable canonical events scoped to a thread
GET/v1/threads/:id/events/streamStream durable canonical events scoped to a thread over SSE
GET/v1/threads/:id/runsList runs for the thread
GET/v1/threads/:id/runs/activeGet the active run for the thread, if any
GET/v1/threads/:id/runs/latestGet the latest run for the thread

POST /v1/threads/:id/messages and POST /v1/runs/:id/inputs accept an optional mode field. queue appends a durable mailbox dispatch, live_then_queue first tries to deliver the messages to the active run and queues only when live delivery is unavailable, steer is an alias for live_then_queue, interrupt_then_queue cancels the active run before queueing, and resume_open_run continues a resumable waiting run.

Thread list cursors are opaque and bound to the query shape that produced them. A bare numeric cursor is accepted only for unfiltered thread listings. Filtered listings, including resource and lineage filters, must continue with the next_cursor returned for the same query. Backend scope filters are not an HTTP parameter; scoped store wrappers inject them internally from ScopeContext.

MethodPathDescription
GET/v1/runsList runs
POST/v1/runsStart a run and stream events over SSE
GET/v1/runs/summaryAdmin-authenticated counts for running, waiting, and created runs; used by the admin dashboard
GET/v1/runs/:idGet a run record
POST/v1/runs/:id/inputsSubmit follow-up input messages as a background run on the same thread
POST/v1/runs/:id/cancelCancel a run by run ID
POST/v1/runs/:id/decisionSubmit a HITL decision by run ID
GET/v1/runs/:id/eventsList durable canonical events scoped to a run
GET/v1/runs/:id/events/streamStream durable canonical events scoped to a run over SSE

Canonical event routes are available when an event store is wired. List routes accept ?cursor= and ?limit= (1..=200, default 50) and return { items, next_cursor, has_more }. Stream routes start from ?cursor=, then from the Last-Event-ID header when present, and otherwise from now.

Event cursors are opaque event-store cursors. A stale cursor returns 410 Gone. SSE frames use the canonical cursor as the SSE id and serialize CanonicalEventHttp as JSON in data.

These return rolling-window snapshots from the RuntimeStatsRegistry published by the observability plugin. Both routes return 503 {"error":"runtime_stats registry not configured"} when the embedder has not wired one — the admin console treats this as a feature flag and shows a friendly notice.

MethodPathDescription
GET/v1/agents/:id/runtime-stats?window=Per-agent snapshot. window is optional (1h, 24h, 7d, <n>s); unset returns the registry’s full retained window
GET/v1/agents/runtime-statsOne snapshot per known agent: { "agents": AgentRuntimeSnapshot[] }

AgentRuntimeSnapshot shape (Rust source: awaken_ext_observability::AgentRuntimeSnapshot):

{
"agent_id": "research",
"window_seconds": 86400,
"bucket_window_seconds": 3600,
"bucket_count": 24,
"inference_count": 12,
"error_count": 0,
"input_tokens": 4180,
"output_tokens": 980,
"avg_inference_duration_ms": 480.5,
"min_inference_duration_ms": 110,
"max_inference_duration_ms": 1820,
"p50_inference_duration_ms": 410,
"p95_inference_duration_ms": 1410,
"p99_inference_duration_ms": 1810,
"inference_duration_histogram": [
{ "upper_bound_ms": 100, "count": 0 },
{ "upper_bound_ms": 250, "count": 1 }
/* ... */
],
"suspensions": 0,
"handoffs": 0,
"delegations": 0,
"tool_calls_by_tool": [
{
"tool": "search",
"call_count": 7,
"failure_count": 0,
"total_duration_ms": 2840,
"avg_duration_ms": 405.7,
"min_duration_ms": 110,
"max_duration_ms": 920,
"p50_duration_ms": 380,
"p95_duration_ms": 880,
"p99_duration_ms": 920
}
]
}

inference_duration_histogram is a value distribution (latency in ms), not a time series. Use the window query parameter for coarse time filtering.

These endpoints are exposed by config_routes(). Read and schema routes require ServerState to be constructed with a config store. Mutation routes additionally require a config runtime manager so writes can validate and publish a new registry snapshot. Without the required config wiring, the routes return 400 with config management API not enabled.

MethodPathDescription
GET/v1/capabilitiesList registered agents, tools, plugins, models, providers, config namespaces, and admin-assistant status/tool metadata
GET/v1/config/:namespaceList entries in a config namespace
POST/v1/config/:namespaceCreate an entry; the body must contain "id"
POST/v1/config/:namespace/validate?id=Dry-run validate. Runs the same prepare_body + schema check as create/update but does not persist or apply. Returns {"ok":true,"normalized":{...}} on success, the same 400/409 errors as a real save on failure. The optional ?id= query lets callers validate an update without going through :id in the path.
GET/v1/config/:namespace/:idGet one config entry
PUT/v1/config/:namespace/:idReplace a config entry
DELETE/v1/config/:namespace/:idDelete a config entry. ?force=true bypasses the dependency check (and audits the override). Returns 409 with {"error":"...","used_by":[...]} when other records depend on this one
POST/v1/config/:namespace/:id/restoreRestore a previous version into the editing store. Body: {"version": "<event-id>"} — the audit-event id of the version to roll back to. Emits a fresh audit event of type restore with restored_from = <event-id>. This does not hot-swap the runtime registry; promote the restored payload with a normal config write after review
GET/v1/config/:namespace/$schemaReturn the JSON Schema for a namespace
GET/v1/config/:namespace/metaList metadata (created_at / updated_at / version / actor) for every entry without returning the full bodies
GET/v1/config/:namespace/:id/metaSingle-entry metadata variant of the above
GET/v1/config/diagnosticsRegistry-wide validation report — surfaces dangling model/provider refs and other cross-entity inconsistencies that per-entity validate would miss
GET/v1/config/providers/:id/removal-previewPreview the agents, models, pools, and providers affected by provider removal before deleting
POST/v1/config/agents/:id/overridesValidate an agent override patch without persisting it
PATCH/v1/config/agents/:id/overridesMerge a partial agent override object. Supports null clears and _clear field lists. Audited as update with overrides payload
DELETE/v1/config/agents/:id/overridesDrop all agent overrides; reverts to the base spec
DELETE/v1/config/agents/:id/overrides/:fieldDrop one overridden field
PATCH/v1/config/tools/:id/overridesPatch a built-in tool’s description. Unknown fields are rejected; empty descriptions and values above 4096 bytes are invalid; null clears the override
DELETE/v1/config/tools/:id/overridesDrop the tool description override
DELETE/v1/config/tools/:id/overrides/:fieldDrop one overridden field of a tool
GET/v1/agents/:id/permission-previewResolve an agent’s effective tool permissions (built-in + plugin + MCP, after include/exclude). Used by the editor’s Tools tab to show “what the LLM will actually see”
GET/v1/agentsConvenience alias for /v1/config/agents
GET/v1/agents/:idConvenience alias for /v1/config/agents/:id
POST/v1/providers/:id/testProbe an existing provider. Returns {"ok": bool, "latency_ms": number, "error"?: string}. The admin console wires this both into the editor and as a per-row “Test” button on the providers list
GET/v1/mcp-servers/:id/statusSee MCP server status below
POST/v1/mcp-servers/:id/restartReconnect a managed MCP server. 202 on success; emits an audit restart event
GET/v1/audit-log?…Query admin audit events. Returns {"items": AuditEvent[], "next_cursor": string?}. 503 {"error":"audit log is not configured"} when audit logging is off. See Admin audit log
GET/v1/admin/assistant/configRead the server-managed Admin Assistant policy prompt and optional model override
PUT/v1/admin/assistant/configSave the editable Admin Assistant policy prompt. The system prompt, bound tools, auth, redaction, and confirmation rules remain server-owned
POST/v1/admin/assistant/runsAdmin-only AI SDK stream for the server-managed Admin Assistant. This is not /v1/ai-sdk/agents/:id/runs and is not exposed as a user Agent

GET /v1/capabilities includes each registered plugin’s config_schemas. The admin console uses this field to render agent-level plugin config forms and save values into AgentSpec.sections. Each entry includes the section key, JSON Schema, optional display metadata, default value, UI schema hints, and an optional editor hint; clients that do not recognize the editor render the JSON Schema form. After a successful create/update/delete or override mutation, the runtime manager publishes a new registry snapshot, so later /v1/runs requests use the updated agents, models, providers, MCP servers, and plugin sections. Restore is the exception: it writes the recovered payload to ConfigStore only, so operators can review rollback state before promoting it through a normal config write.

capabilities.admin_assistant.bound_tools lists the assistant’s locked admin-only tools for UI comparison. Those tools are directly bound by /v1/admin/assistant/runs, are not returned in capabilities.tools, and cannot be assigned to normal AgentSpecs. The built-in tool set can read platform capabilities, create/publish an AgentSpec, create a draft without publishing, and validate a draft. The assistant itself is not stored as a normal registry agent; when admin_assistant.config.model_id is unset, the server selects the first provider-backed configured model and reports that selection as capabilities.admin_assistant.model_id.

Current built-in namespaces:

  • agents
  • models
  • model-pools
  • providers
  • mcp-servers
  • skills
{
"connected": true,
"last_error": null, // string when last health attempt failed
"tools": [
{ "name": "search", "description": "Search the web." }
],
"consecutive_failures": 0, // streak since last success
"last_attempt_at": 1777708820, // unix seconds, null until first probe
"last_success_at": 1777708820, // unix seconds, null until first success
"reconnecting": false,
"permanently_failed": false, // true once the manager has given up
"session_generation": 2, // HTTP session reset/reinitialize generation
"transport_reconnect_count": 0, // successful runtime re-creations
"last_init_at": 1777708820 // unix seconds, null before initialize
}

consecutive_failures + last_success_at are surfaced from the existing McpRefreshHealth budget. There is no separate “errors in last 24h” counter — the health budget is the source of truth.

The raw HTTP MCP-Session-Id is intentionally not exposed by this endpoint. transport_reconnect_count counts runtime tear-down/recreate cycles; HTTP 404 session reset churn is visible through session_generation and last_init_at.

AuditEvent:

{
"id": "01HXJK...", // ULID
"ts": "2026-05-02T07:58:14.900Z", // RFC 3339
"actor": "<sha256-prefix>", // SHA-256 of bearer token, optionally
// suffixed with the X-Awaken-Actor label
"action": "create" | "update" | "delete" | "restart" | "publish" | "restore",
"resource": "agents/research", // "<namespace>/<id>"
"before": { /* spec snapshot */ },
"after": { /* spec snapshot */ },
"ip": "127.0.0.1",
"request_id": null,
"restored_from": null // event id this restore is rolling back to
}

Filters: ?resource=, ?action=, ?actor=, ?since=, ?until=, ?limit= (clamped to [1, 1000]), ?cursor= for pagination.

Trace routes are admin-authenticated and mounted only when AdminApiConfig.expose_trace_routes is true and a trace module is wired. They can expose prompts, tool arguments, and model responses; the default is false. Unknown query fields are rejected.

MethodPathDescription
GET/v1/traces?agent_id=&prompt_id=&experiment_id=&variant_name=&since=&limit=List traceable runs
GET/v1/traces/:run_id?offset=&limit=Return one page of trace events as NDJSON with pagination headers
POST/v1/traces/:run_id/pinPin a trace for later review or fixture curation

since is RFC 3339. limit=0 is rejected. Trace event pages cap limit at 1000 and return pagination metadata in x-trace-total-events and x-trace-next-offset. Trace event responses are NDJSON.

Eval routes are admin-authenticated and mounted when AdminApiConfig.expose_eval_routes is true and the config/eval modules are wired. Request limits are controlled by ServerConfig.eval_limits.

MethodPathDescription
GET/v1/eval/datasetsList eval datasets
POST/v1/eval/datasetsCreate an eval dataset
GET/v1/eval/datasets/:idGet one dataset
PUT/v1/eval/datasets/:idReplace one dataset
DELETE/v1/eval/datasets/:idDelete one dataset; supports revision checks
POST/v1/eval/datasets/:id/itemsCurate trace/dialogue input into dataset fixtures
POST/v1/eval/datasets/:id/fixturesAppend fixtures directly
POST/v1/eval/datasets/:id/import-tracesImport traces into fixtures
POST/v1/eval/datasets/:id/import-dialogueImport dialogue into fixtures
GET/v1/eval/runs?dataset_id=&limit=List eval runs
POST/v1/eval/runsStart a dataset eval run
GET/v1/eval/runs/:idGet an eval run and its item reports
POST/v1/eval/onlineRun an ad-hoc online eval without saving a dataset

Eval run list accepts dataset_id, limit, cursor, since_secs, until_secs, and aggregate=samples. Run detail accepts baseline=<run_id> to include baseline comparison fields. Dataset DELETE/PUT-style mutations use revision checks when the request includes a revision. Dataset import routes accept trace/dialogue selectors plus optional caps; omitted trace import caps fall back to ServerConfig.eval_limits.default_import_traces_max.

POST /v1/eval/runs starts scripted or live dataset evaluation. samples and matrix size are validated against ServerConfig.eval_limits; live execution requires runnable runtime/provider wiring. POST /v1/eval/online uses the same limit model for ad-hoc prompts without creating a dataset.

MethodPathDescription
POST/v1/ai-sdk/chatStart a chat run and stream protocol-encoded events
POST/v1/ai-sdk/agent-previews/runsRun a draft AgentSpec without saving it; used by the admin console preview
POST/v1/ai-sdk/threads/:thread_id/runsStart a thread-scoped AI SDK run
POST/v1/ai-sdk/agents/:agent_id/runsStart an agent-scoped AI SDK run
GET/v1/ai-sdk/chat/:thread_id/streamResume an SSE stream by thread ID
GET/v1/ai-sdk/threads/:thread_id/streamAlias for stream resume by thread ID
GET/v1/ai-sdk/threads/:thread_id/replayReplay durable AI SDK protocol frames by thread ID
GET/v1/ai-sdk/chat/:thread_id/replayAlias for durable AI SDK protocol replay
GET/v1/ai-sdk/threads/:thread_id/messagesList thread messages
POST/v1/ai-sdk/threads/:thread_id/cancelCancel the active or queued run on a thread
POST/v1/ai-sdk/threads/:thread_id/interruptInterrupt a thread (bump dispatch epoch, supersede pending dispatches, cancel active run)

AI SDK numeric Last-Event-ID values are live replay-buffer positions. Durable protocol replay uses opaque protocol replay cursors from the replay endpoints. Replay endpoints accept ?cursor= or Last-Event-ID, plus ?limit= (default 100, max 500). Missing replay storage returns 503, malformed cursors return 400, and expired cursors return 410 Gone.

MethodPathDescription
POST/v1/ag-ui/runStart an AG-UI run and stream AG-UI events
POST/v1/ag-ui/threads/:thread_id/runsStart a thread-scoped AG-UI run
POST/v1/ag-ui/agents/:agent_id/runsStart an agent-scoped AG-UI run
POST/v1/ag-ui/threads/:thread_id/interruptInterrupt a thread
GET/v1/ag-ui/threads/:thread_id/replayReplay durable AG-UI protocol frames by thread ID
GET/v1/ag-ui/threads/:id/messagesList thread messages

AG-UI replay has the same cursor, limit, 503, 400, and 410 semantics as AI SDK replay.

MethodPathDescription
GET/.well-known/agent-card.jsonGet the public/default agent card
POST/v1/a2a/message:sendSend a message to the public/default A2A agent
POST/v1/a2a/message:streamStreaming send over SSE
GET/v1/a2a/tasksList A2A tasks
GET/v1/a2a/tasks/:task_idGet task status
POST/v1/a2a/tasks/:task_id:cancelCancel a task
POST/v1/a2a/tasks/:task_id:subscribeSubscribe to task updates over SSE
POST/v1/a2a/tasks/:task_id/pushNotificationConfigsCreate a push notification config
GET/v1/a2a/tasks/:task_id/pushNotificationConfigsList push notification configs
GET/v1/a2a/tasks/:task_id/pushNotificationConfigs/:config_idGet a push notification config
DELETE/v1/a2a/tasks/:task_id/pushNotificationConfigs/:config_idDelete a push notification config
GET/v1/a2a/extendedAgentCardGet the extended agent card; returns 501 unless enabled
POST/v1/a2a/:tenant/message:sendSend a message to a tenant-scoped agent
POST/v1/a2a/:tenant/message:streamTenant-scoped streaming send
GET/v1/a2a/:tenant/tasksList tasks for a tenant-scoped agent
GET/v1/a2a/:tenant/tasks/:task_idGet tenant-scoped task status
POST/v1/a2a/:tenant/tasks/:task_id:cancelCancel a tenant-scoped task
POST/v1/a2a/:tenant/tasks/:task_id:subscribeSubscribe to tenant-scoped task updates
POST/v1/a2a/:tenant/tasks/:task_id/pushNotificationConfigsCreate a tenant-scoped push notification config
GET/v1/a2a/:tenant/tasks/:task_id/pushNotificationConfigsList tenant-scoped push notification configs
GET/v1/a2a/:tenant/tasks/:task_id/pushNotificationConfigs/:config_idGet a tenant-scoped push notification config
DELETE/v1/a2a/:tenant/tasks/:task_id/pushNotificationConfigs/:config_idDelete a tenant-scoped push notification config
GET/v1/a2a/:tenant/extendedAgentCardGet the tenant-scoped extended agent card
MethodPathDescription
POST/v1/mcpMCP JSON-RPC request/response endpoint. initialize creates a session and returns MCP-Session-Id; later requests, notifications, and responses require that header.
GET/v1/mcpReserved for MCP server-initiated SSE; currently returns 405
DELETE/v1/mcpTerminate a known MCP HTTP session identified by MCP-Session-Id; returns 204 or 404

initialize requests must not include MCP-Session-Id. tools/call may stream responses. All MCP HTTP routes validate Origin when present.

Pagination applies to route families that document offset, limit, or cursor; it is not automatically accepted by every endpoint.

  • offset — number of items to skip
  • limit — maximum items to return, clamped to 1..=200 (default 50)
  • cursor — opaque pagination cursor; when provided it takes precedence over offset. Cursors are bound to the original query shape and rejected if any filter changes between requests
  • next_cursor / prev_cursor — returned in the response body when more pages exist

Thread list filters (/v1/threads, /v1/threads/summaries):

  • resource_id (alias resourceId) — filter by external resource grouping
  • parent_thread_id (alias parentThreadId) — restrict to direct children of this parent thread
  • root — when true, restrict to root threads with no parent. Cannot be combined with parent_thread_id

Message list filters (/v1/threads/:id/messages and the protocol-specific aliases):

  • after, before — sequence-number window
  • orderasc (default) or desc
  • visibilityexternal (default), internal, or all
  • run_id (alias runId) — restrict to messages produced by this run

Run list filters:

  • statuscreated, running, waiting, or done

Most route groups return:

{ "error": "human-readable message" }

MCP routes return JSON-RPC error objects instead of the generic shape above.