HTTP API reference¶
The engine exposes a loopback-first API under http://{host}:{port}/api/v1. Read runtime.json from the engine data directory to discover the current host, port, and bearer token; do not hard-code production values.
Authentication and conventions¶
GET /health is public. Every other operation below requires Authorization: Bearer <runtime-token>. An invalid or missing token returns 401 {"error":"Missing or invalid bearer token."}. Authentication uses constant-time comparison.
Most typed REST objects use camelCase on the wire and accept camelCase or snake_case input. Account objects and most WebSocket data use snake_case. RAG results intentionally contain mostly snake_case fields plus rootIndex. Timestamps are ISO-8601 UTC strings.
A shared schema failure is 400 {"error":"Invalid request body.","details":"..."}. Settings, models, tools, and RAG use 400 {"error":"Invalid request body."} without details. Resource misses normally use 404 {"error":"<Resource> not found."}, but some endpoints intentionally return an empty body or collection; those cases are listed below.
PUT does not have one universal meaning
Workspace PUT is a full replacement and requires name. Thread PUT is patch-like: omitted fields are preserved and explicit null clears them. Model PUT requires provider/model and has field-specific credential preservation. Tool PUT has its own preservation rules.
Request and response objects referenced below are defined in Schemas.
System and account¶
| Method and path | Request | Success | Errors and notes |
|---|---|---|---|
GET /health |
None; public | 200 Health |
The only public operation. |
GET /account/session |
None | 200 AccountSession |
Cloud/secure-store AccountError status with Error; an expired/rejected saved session becomes a logged-out 200. |
POST /account/login |
AccountLogin |
200 AccountSession |
400 if email/password are missing or blank; cloud status or 502 may pass through. |
POST /account/signup/code |
SignupCodeRequest |
200 {notice} |
400 for missing/blank email; cloud/account error otherwise. |
POST /account/signup/complete |
SignupCompleteRequest |
200 AccountSession |
400 for missing/blank email, code, or password. |
POST /account/logout |
Body ignored | 200 {authenticated:false,user:null} |
Local credentials are cleared even when remote logout fails. |
POST /agui |
Body ignored | 200 {message:"AG-UI endpoint not yet implemented"} |
Placeholder only; do not integrate against it. |
Workspaces, threads, and messages¶
| Method and path | Request | Success | Errors and notes |
|---|---|---|---|
GET /workspaces |
None | 200 Workspace[], ordered by name |
— |
POST /workspaces |
CreateWorkspace |
200 Workspace |
400 schema error; uses an existing network when present (selection is unordered), otherwise creates default. |
GET /workspaces/{uuid} |
uuid path string |
200 Workspace |
404 Error. |
PUT /workspaces/{uuid} |
CreateWorkspace |
200 Workspace |
Full replacement; omitted nullable fields become null; 400/404. |
DELETE /workspaces/{uuid} |
uuid |
204 empty |
404 Error; cascades database children, not filesystem roots/sidecars. |
GET /workspaces/{uuid}/threads |
uuid |
200 Thread[], newest-updated first |
Unknown workspace returns 200 []. |
POST /threads |
CreateThread |
200 Thread |
400; unknown workspace returns 404 Error; publishes thread.created. |
GET /threads/{uuid} |
uuid |
200 Thread |
404 Error. |
PUT /threads/{uuid} |
UpdateThread |
200 Thread |
Patch-like; {} is valid and refreshes updatedAt; 400/404; publishes thread.updated. |
DELETE /threads/{uuid} |
uuid |
204 empty |
404 Error; cascades messages. |
GET /threads/{uuid}/messages |
uuid |
200 Message[], oldest first |
Unknown thread returns 200 []. |
POST /messages |
CreateMessage |
200 Message |
400; unknown thread 404 Error; updates thread time and publishes message.created. |
See Workspaces and threads before building client-side selection, model, tool, approval, or deletion behavior.
Workspace files¶
All file operations use {uuid} plus a zero-based rootIndex from the workspace’s directories JSON. Relative paths cannot be absolute, contain empty/./.. segments, escape the root, or cross a symlink/junction/reparse point. Text is UTF-8 and limited to 1,000,000 bytes. Common failures are 400 invalid root/path/schema/type, 403 inaccessible or reparse path, 404 missing workspace/path, and 409 collision.
| Method and path | Parameters/request | Success | Errors and notes |
|---|---|---|---|
GET /workspaces/{uuid}/files/search |
Query q default ""; limit integer default 20, clamped 1–50 |
200 FileSearchMatch[] |
Searches all roots, at most 25,000 visited files/root; invalid limit 400. |
GET /workspaces/{uuid}/files |
Required query rootIndex; optional path (root when omitted) |
200 FileEntry[] |
Directories first, then case-insensitive name. |
GET /workspaces/{uuid}/files/content |
Required rootIndex and non-empty path |
200 FileContent |
Allowlisted text extensions only; directory, unsupported type, non-UTF-8, or >1 MB is 400. |
GET /workspaces/{uuid}/files/preview |
Required rootIndex, path |
Raw 200 image/PDF/DOCX bytes |
Validates signatures/archive safety; response headers include X-Preview-Kind; limits are 20/50/25 MiB. |
POST /workspaces/{uuid}/files/content |
Query rootIndex,path; body FileContent |
200 FileContent |
Create only; null/missing content 400, existing target 409. |
PUT /workspaces/{uuid}/files/content |
Query rootIndex,path; body FileContent |
200 FileContent |
Replaces existing text file; missing target 404. |
POST /workspaces/{uuid}/files/directory |
Query rootIndex,path; body ignored |
200 FileEntry |
Creates one directory; parent must exist; collision 409. |
POST /workspaces/{uuid}/files/move |
MovePath |
200 FileEntry |
Destination is an existing directory and basename is retained; cross-root/self/descendant move 400; collision 409. |
There is no workspace-file delete endpoint. Search skips .git, .hg, .svn, .subconscious, .dart_tool, node_modules, build, dist, bin, obj, __pycache__, .venv, and venv.
Settings¶
| Method and path | Request | Success | Errors and notes |
|---|---|---|---|
GET /settings |
Optional exact-match query filters key, tag, client |
200 Setting[], database order |
Omitted filters are unrestricted; null scope and empty string are distinct. |
PUT /settings |
Non-empty Setting[] |
200 Setting[] |
Upserts exact (key,tag,client) scope; blank key, duplicate scope, or invalid array 400. |
Models¶
Encrypted-store failures return 503 application/problem+json with ProblemDetails. Credentials are write-only and never appear in responses.
| Method and path | Request | Success | Errors and notes |
|---|---|---|---|
GET /model-configurations |
None | 200 ModelConfiguration[], sorted by ID |
503 encrypted-store failure. |
POST /model-configurations |
UpsertModelConfiguration |
201 ModelConfiguration; Location header |
Blank provider/model or non-positive context window 400; 503. |
PUT /model-configurations/{id} |
Complete UpsertModelConfiguration |
200 ModelConfiguration |
Empty 404 if absent; omitted API key is preserved; metadata fields are replaced/cleared; 400/503. |
DELETE /model-configurations/{id} |
id |
Empty 204 |
Empty 404 if absent; 503. |
GET /models |
None | 200 Model[] |
Configured catalog followed by the credential-free echo development model; 503. |
Chat resolves a model by exact configuration ID, then by the first configuration whose model name matches. Duplicate model names are therefore ambiguous; clients should send IDs.
Tools¶
The configured registry stores metadata and encrypted API-key headers. Its records are not the same as client-registered WebSocket UI tools. In the currently audited chat path, HTTP configured-tool records are configuration metadata and are not directly executed by the built-in WebSocket tool registry.
| Method and path | Request | Success | Errors and notes |
|---|---|---|---|
GET /tools/catalog |
None | 200 ToolCatalog |
Built-in groups plus configured records; 503 store failure. |
GET /tool-registry |
None | 200 ToolConfiguration[] |
Sorted by alias/name; 503. |
POST /tool-registry |
UpsertToolConfiguration |
201 ToolConfiguration; Location header |
Alias or name required; type/auth/URL/header validation; 400/503. |
GET /tool-registry/{uuid} |
uuid |
200 ToolConfiguration |
Empty 404; 503. |
PUT /tool-registry/{uuid} |
UpsertToolConfiguration |
200 ToolConfiguration |
Empty 404; see field preservation in schemas; 400/503. |
DELETE /tool-registry/{uuid} |
uuid |
Empty 204 |
Empty 404; encrypted auth is also removed; 503. |
Retrieval (RAG)¶
Roots are validated when used and processed sequentially under per-root locks. A root is disabled when ragConfig.roots contains a matching {path,enabled:false} entry.
| Method and path | Request | Success | Errors and notes |
|---|---|---|---|
POST /workspaces/{uuid}/rag/index |
Body ignored | 200 RagWorkspaceIndex |
Indexes every enabled root; workspace 404, invalid roots 400, embedding failure 503. |
POST /workspaces/{uuid}/rag/roots/{root_index}/index |
Integer path root_index; body ignored |
200 RagRootIndex |
Malformed 400, missing root 404, disabled root 409, embedding failure 503. |
POST /workspaces/{uuid}/rag/search |
RagSearch |
200 RagResult[] |
Mode is case-sensitive keyword, vector, or hybrid; schema/root 400, workspace 404, embedding 503. |
Search silently skips roots without a sidecar or with an incompatible embedding signature. Sensitive-path matches return metadata-only placeholder content.
Event stream¶
| Method and path | Request | Success | Notes |
|---|---|---|---|
GET /stream |
None | 200 text/event-stream |
Future events only; no heartbeat, IDs, or replay. See Realtime API. |
The bidirectional WS /api/v1/events route is documented separately because it uses frames rather than HTTP request/response operations.