HTTP API

🔵 The server exposes an HTTP control plane plus a chat endpoint that streams Server-Sent Events (SSE). The list of endpoints is printed at startup in the log.


8.1 Endpoints

Pages and static assets (public)

Endpoint Description
GET / markdown site (landing)
GET /site/ · GET /site/:page same site under a subpath
GET /sessions · /budgets · /teams · /users · /cron · /workflows · /dashboard admin UI
GET /agents/:name single agent page
GET /widget · /widget.js · /dist/* widget and its assets
GET /demo · /demo/overlay widget demo / overlay on top of live sites
GET /distrib/* distribution archives (linux/windows zip + sha256, android apk)
GET /proxy?url=… · /proxy/content?url=… HTTP proxy for the frontend
GET /login · POST /login · POST /logout web login for admin UI
GET /login/oidc/:provider · GET /login/oidc/:provider/callback OIDC login for admin UI (Google/Yandex/Tinkoff)
GET /v1/login/methods list of enabled OIDC providers
GET /v1/login/widget/:provider · GET /v1/login/widget/:provider/callback OIDC login of the end user in the widget (popup)
POST /v1/login/widget/password end-user login via auth.users (issues x-user-token)
GET /v1/connectors/callback OAuth connector callback (Google Drive, etc.)
GET /v1/allowed-senders/:template · POST /v1/allowed-senders/:template dynamic sender allowlist (list · add) 🔒
DELETE /v1/allowed-senders/:template/:identity remove a sender from the allowlist 🔒

Public API

Endpoint Description
GET /health healthcheck
GET /v1/models available models
GET /v1/templates agent templates (welcome, tools; tools_declared/loaded/failed and hooks_* — what actually loaded)
POST /v1/chat/completions chat with an agent (SSE, custom format)
POST /v1/continuations next page of a lazily paginated table
POST /v1/uploads upload attachments (file/screenshot/photo) into the session scratch
POST /v1/input-response response to an agent's attachment request (request_attachment)
POST /v1/approval-response end-user response to an inline approval (widget)
POST /v1/sessions/:id close the session (public — the widget sends sendBeacon)
POST /v1/inbound/:template one-shot agent turn from an external system (JSON, optionally bearer)
POST /v1/user-ticket issue an HMAC-signed identity token
POST /v1/feedback like/dislike on a response
POST /v1/workflows/:name/webhook webhook workflow trigger (protected by the workflow token)
/ext/** (custom) http_route / static_route — user-defined REST endpoints and static assets added globally (config/routes/) or by agents (route_files:); auth public/bearer/token_env/user per route (see §21)

Admin API 🔒

Endpoint Description
GET /v1/agents · POST /v1/agents list agents · create agent
GET /v1/agents/:name snapshot of a single agent
DELETE /v1/agents/:pid kill an agent and its descendants
POST /v1/agents/:pid/signal steering: pause / resume / interrupt / terminate (body {"signal":"…"}) 🔒
POST /v1/agents/:pid/reset lift supervision quarantine (unpause + delete dead-letters) 🔒
GET /v1/dead-letters?limit=N quarantined agent inputs (dead-letter)
POST /v1/agents/:name/bench/run run a template benchmark
GET /v1/agents/:name/bench/runs · /bench/runs/:id benchmark history and detail
GET /v1/sessions list sessions (?user_id= — sessions of a single user + user_stats)
GET /v1/sessions/:id · DELETE /v1/sessions/:id session detail · close
GET /v1/budgets budget groups and processes
GET /v1/machines · POST /v1/machines inventory of managed machines · create/update (module remote) 🔒
GET /v1/machines/:slug · PUT /v1/machines/:slug · DELETE /v1/machines/:slug machine · update (slug from the path) · delete 🔒
GET /v1/kernels · POST /v1/kernels registry of remote Agent OS cores · create/update (module kernels) 🔒
GET /v1/kernels/:slug · PUT /v1/kernels/:slug · DELETE /v1/kernels/:slug core · update · delete 🔒
GET /v1/kernels/:slug/health HTTP health probe of a remote core 🔒
GET /v1/cron · DELETE /v1/cron/:name cron jobs and history · delete
GET /v1/workflows · POST /v1/workflows/:name/run workflow · run
GET /v1/workflows/runs/:id · /runs/:id/events workflow run · its events
GET /v1/dashboard system dashboard (load, tokens, series)
GET /v1/feedback/stats ratings statistics
GET /v1/users channel → user mappings (with alias/operator note)
PATCH /v1/users/:id set/clear {alias, note} of the canonical user
POST /v1/users/link · POST /v1/users/:id/unlink merge/split identities
GET /v1/memory/:scope inspect the long-term memory of a scope
GET /v1/hooks/metrics hook metrics: calls/panics/timeouts/duration by (hook, point)
GET /v1/circuit-breakers state of provider breakers (status, failures, opened) + config
POST /v1/circuit-breakers/:name/reset force-close an open breaker
GET /v1/approvals queue of pending approvals (operator inbox)
POST /v1/approvals/:request_id approve/reject {approved, comment}
GET/POST /v1/approvals/roles logical approver roles · create/update
DELETE /v1/approvals/roles/:name delete a role
POST/DELETE /v1/approvals/roles/:name/members… add/remove a role member
GET /v1/system/tree platform introspection; navigation by chunks: ?view=summary|modules|types|graph, ?module=, ?type=, ?registry= (no parameters — the whole tree)
GET /v1/system/modules · /registries · /types · /routes · /services introspection slices
POST /v1/platform/modules/:id/enable · /disable enable/disable a module (applied after restart)
POST /v1/platform/restart restart the server to apply the module set
GET /v1/builder/agents list of config agents (name, presence of agent.yaml, number of files, protected)
GET /v1/builder/agents/:name bundle metadata: agent.yaml + list of files {path,size} (without contents)
GET/PUT/PATCH/DELETE /v1/builder/agents/:name/file?path= single file: read · write (raw body) · replace line {old,new,replace_all} · delete
PUT /v1/builder/agents/:name create/partially update a bundle (agent_yaml? and/or files?)
DELETE /v1/builder/agents/:name delete an agent directory
POST /v1/builder/agents/:name/yaml?path= YAML edit of file {op,key,value}, op = set|append|remove
POST /v1/builder/validate validate a bundle without writing (including checking each tool_files/hook_files file for the presence of kind)
GET /v1/tools · GET /v1/tools/:name · POST /v1/tools/:name/call · POST /v1/tools/run tool registry: list · schema · run by name · run inline YAML
POST /v1/agents/:name/tools/:tool/call run a tool in the environment of agent :name (its scope/state, session if alive), body {args}
GET /v1/state/:scope/:id scoped-state keys :scope (agent|user|session|team) for :id
GET/PUT/DELETE /v1/state/:scope/:id/:key read · write {value} · delete a single state key
GET/POST /v1/teams · GET/DELETE /v1/teams/:id commands: list (?user_id= — user commands + roles) · create · detail (participants+grants) · delete (cascade with state)
POST/DELETE /v1/teams/:id/members… · /grants… team participants and grants (admin)
POST/DELETE /v1/my/teams/:id/… team owner self-service (only within own grants)
GET /v1/docs documentation table of contents as JSON (languages, sections/pages {id,title})
GET /v1/docs/page?id=&lang= documentation page as markdown {id,title,lang,markdown}
GET /v1/bench/runs · /v1/bench/catalog runs across all templates · templates with configured scenarios

🔒 = requires Authorization: Bearer <токен> when admin-auth is configured (see §8.3).

Pagination

List endpoints (/v1/sessions, /v1/agents, /v1/templates, /v1/budgets) accept ?limit=N&offset=M. limit — page size (clamped to 1–1000, default 100), offset — how many rows to skip (default 0). Responses include total (and /v1/sessions — also history_total). /v1/sessions adds history_truncated: true when there are more past sessions than the current page.


8.2 Chat completions

POST /v1/chat/completions streams Server-Sent Events (SSE). The path and request shape are similar to OpenAI, but the response format is its own — it is not compatible with the OpenAI SDK (no choices[].delta, no data: [DONE]).

curl http://127.0.0.1:3000/v1/chat/completions \
  -H 'Content-Type: application/json' \
  -d '{
    "model": "ngu-agent",
    "messages": [{"role": "user", "content": "Как поступить в НГУ?"}]
  }'

Request fields:

Field Required Meaning
model yes template name (ngu-agent, drom-agent, …) or built-in assistant
messages yes list of {role, content}; roles user and system enter the context
session no session token — one session = one isolated agent. Omit — legacy mode (shared template agent)
attachments no list of [{name, mime, size}] files already uploaded via POST /v1/uploads into the session scratch — the server will inject a message with their description
stream no accepted for compatibility, ignored — always streaming
temperature no accepted, ignored
max_tokens no accepted, ignored

The X-User-Id header sets user_id for per-user budgets and memory scope. When auth.web_user_secret_env is configured, the server requires an HMAC-signed X-User-Token (from POST /v1/user-ticket); see §8.8.

Response (SSE events)

The body is a stream of named SSE events; done is the only one that is always present.

event Payload data
token {"content": "<токен>", "final": bool}
tool_call {"name": "<инструмент>", "body": "<рендер вызова>"}
tool_progress {"name": "<инструмент>", "body": "<прогресс>"}
tool_result {"name": "<инструмент>", "body": "<рендер результата>"}
result_blocks `{"name": "<инструмент>", "blocks": [ {type: "text"
input_request {"request_id": "<id>", "kind": "file"|"screenshot"|"photo", "description": "..."} — the agent requested an attachment; respond via POST /v1/input-response
budget {"tokens": n, "prompt": n, "completion": n, "cached": n, "total": n, "cost": "$x.xxxx"}
done {} — stream finished

result_blocks carries the visual layer of the tool (charts, tables, text, files); the table block may include continuation: {id} — POST /v1/continuations with this id will return the next page of blocks, and the file block carries name, mime and data (base64) for download. See common fields.

Errors (unknown model, session error, budget exhaustion, rate-limit) are reported as token events inside 200, not as an HTTP error code.

8.2.1 Attachments (uploads / input-response)

Uploading files from the user (widget, Android) into the session scratch directory — POST /v1/uploads (multipart/form-data):

The response is {"session_id": "...", "files": [{"name", "mime", "size"}, …]}. Files are written to <scratch>/<session>/, then the agent reads them by name (file_tool temp, shell_tool {{temp_dir}}, vision_tool). The template's attachments.enabled/max_size is taken into account; if attachments are disabled — 400.

An agent attachment request (tool request_attachment) arrives as an SSE event input_request; the widget shows a prompt, the user attaches a file, it is uploaded via /v1/uploads, after which the widget resolves the request:

curl http://127.0.0.1:3000/v1/input-response \
  -H 'Content-Type: application/json' \
  -d '{"request_id": "<id>", "files": [{"name": "photo.jpg", "mime": "image/jpeg", "size": 12345}]}'

An empty files = the user declined. Unknown/expired request_id — 404; default wait is 300 sec (the tool's timeout_secs field).

8.2.2 Attention and push

When the agent calls notify_tool (or a push-sync triggers), the server publishes an attention event to all connected clients of the user.

GET /v1/attention/stream — a long-lived SSE stream of attention events (x-user-id / x-user-token, like chat). The client (Android foreground service, web widget) keeps the connection open and renders the notification itself; this is the live path.

event: attention
data: {"title":"...","body":"...","level":"alert","require_ack":false,"session_id":"...","template":"...","channels":[]}

level levels: silent | notify | alert | alarm | call. alarm/call (or require_ack: true) mean "repeat until acknowledged" — Android shows a full-screen alarm/ring, the widget — a banner with sound.

Method Path Meaning
GET /v1/attention/stream SSE attention stream for the user
POST /v1/push/register {kind, endpoint} — register a device push endpoint (UnifiedPush/ntfy) for wakeup
POST /v1/push/unregister {kind, endpoint} — remove the endpoint

If there are no live subscribers at the moment of publication, the server sends a wakeup to the registered endpoints (without Google — via UnifiedPush/ntfy), and the killed app wakes up and shows the notification. Details and an example — config/agents/notify-demo/README.md.


8.3 Auth

🔒 endpoints accept either Authorization: Bearer <токен> (for API clients), or the agentos_session cookie (for the browser after web login).

If auth is configured but no credential resolves — the admin API returns 401 for every request (fail-closed). Without tokens and users, the admin API is open (dev mode).

# API-клиент (bearer)
curl -H 'Authorization: Bearer <token>' http://127.0.0.1:3000/v1/sessions

# Web-логин (браузерный поток)
curl -c cookies.txt -H 'Content-Type: application/json' \
  -d '{"username":"admin","password":"<password>"}' \
  http://127.0.0.1:3000/login
curl -b cookies.txt http://127.0.0.1:3000/v1/sessions

8.4 Embedding the widget

<script src="http://127.0.0.1:3000/widget.js"></script>

The widget is served from /widget, communicates with /v1/chat/completions, and closes sessions via POST /v1/sessions/:id (sendBeacon). A full breakdown of the widget (configuration, model selection, demo) — in §19.


8.5 Proxy

/proxy and /proxy/content download URLs for the frontend. Restrictions:


8.6 Healthcheck

curl http://127.0.0.1:3000/health

8.7 HTTPS / TLS

Set AGENT_OS_TLS_CERT and AGENT_OS_TLS_KEY (paths to PEM) — the server will bring up HTTPS. Otherwise — plain HTTP on AGENT_OS_BIND (default 127.0.0.1:3000).


8.8 Incoming webhook and identity

Incoming webhook

POST /v1/inbound/:template — an external system (CRM, ERP, monitoring, form backend, …) starts a single agent turn and synchronously reads the response — without SSE, without an array of messages.

curl -X POST http://127.0.0.1:3000/v1/inbound/ngu-agent \
  -H 'Content-Type: application/json' \
  -d '{"message":"какие общежития у НГУ?","session_id":"crm-123"}'

Request:

Field Required Meaning
message yes user message
session_id no stable id for reusing context; omitted = new session
user_id no end-user identity (Origin::Webhook); omitted = anonymous

Response: {"answer": "...", "status": "success", "session_id": "...", "cost_usd": 0.0, "prompt_tokens": n, "completion_tokens": n, "data": null}.

Auth — bearer token when inbound.token_env is set (empty = open, dev).

End-user identity tokens

When auth.web_user_secret_env points to an env with an HMAC secret, the widget receives a signed token and sends it as X-User-Token instead of the self-declared X-User-Id:

curl -X POST http://127.0.0.1:3000/v1/user-ticket \
  -H 'Content-Type: application/json' -d '{"user_id":"cw-browser-id"}'
# → {"user_id":"cw-browser-id","token":"cw-browser-id.<hmac>"}

Without a secret, the server trusts X-User-Id directly (dev mode).

Canonical user identities

GET /v1/users lists mappings of (origin, external_id) → canonical_id; the operator can merge two channel ids (e.g. web-id and Telegram-id) so that they share memory, budget, and state:

curl -X POST http://127.0.0.1:3000/v1/users/link \
  -H 'Authorization: Bearer <token>' \
  -d '{"survivor":"cw-browser-id","merged":"tg:123"}'

The merge migrates the scoped state store, long-term memory, and in-memory budget buckets; subsequent sessions of both channels resolve to survivor.


8.9 Feedback

The widget sends answer ratings (like / dislike / cleared) to POST /v1/feedback (public), and statistics — to GET /v1/feedback/stats (🔒).

A rating closes the quality loop: the memory hook on a like/dislike raises/lowers importance of the facts recalled in that turn (§6.6). The request body includes value, user_text, session.