API Reference¶
This reference describes the data APIs and WebSocket endpoint registered by
src/numi_chat/web/app.py; HTML pages and static assets are outside its scope.
In development (ENVIRONMENT=development), FastAPI publishes
/openapi.json, /docs, and /redoc. Those routes are disabled outside
development. For the turn lifecycle, see
Message Flow and Persistence.
Base URL and authentication¶
The local server listens on http://localhost:4567 by default. The API prefix
is /api/v1, except for health checks, generated files, and the WebSocket endpoint.
HTTP requests may authenticate with either:
or the access_token HttpOnly cookie set by the web UI. Refresh tokens are
returned by login/register; the server also sets their HttpOnly cookies.
An access JWT must match an unexpired, unrevoked session record. See
Authentication
for session behavior.
Authentication failures return 401; missing admin privileges return 403.
Rate-limited endpoints return 429 and may include Retry-After.
Authentication endpoints¶
All paths below are under /api/v1/auth.
| Method | Path | Auth | Purpose |
|---|---|---|---|
GET |
/registration-status |
No | Check whether registration is enabled |
POST |
/register |
No | Create the first or an enabled user account |
POST |
/token |
No | Log in with OAuth2 form fields username and password |
POST |
/refresh |
No | Exchange a refresh token for a new token pair |
POST |
/logout |
No | Revoke the refresh cookie and clear auth cookies |
POST /register accepts JSON with username and password. Usernames are
3–50 characters matching [A-Za-z0-9_-]+; passwords must be at least 12
characters and also satisfy the configured complexity policy.
POST /token uses application/x-www-form-urlencoded fields. Successful token
responses have this shape:
POST /refresh accepts {"refresh_token": "..."} or the refresh cookie
and rotates the token pair. POST /logout revokes the refresh token from the
cookie; a bearer header alone does not supply that token.
On an empty database, registration is temporarily allowed for the first user; that user becomes an administrator. Later registration requires the application setting to be enabled by an administrator.
Chat endpoints¶
All chat endpoints require authentication. {chat_id} is the external chat UUID.
| Method | Path | Purpose |
|---|---|---|
GET |
/api/v1/chats/ |
List chats; query offset (default 0), limit (1–100, default 20), and optional archived (boolean) |
GET |
/api/v1/chats/search |
Search chats with query q and optional archived filter; returns matched message snippets |
POST |
/api/v1/chats/ |
Create a chat; returns 201 and the chat summary |
PATCH |
/api/v1/chats/{chat_id}/organization |
Update chat organization (pinned, archived) |
DELETE |
/api/v1/chats/{chat_id} |
Delete a chat and owned data |
GET |
/api/v1/chats/{chat_id}/messages |
Load chat messages |
POST |
/api/v1/chats/{chat_id}/title |
Update the title |
POST |
/api/v1/chats/{chat_id}/model |
Change the model before messages exist |
POST |
/api/v1/chats/{chat_id}/reasoning |
Set the reasoning level |
POST |
/api/v1/chats/{chat_id}/tools |
Set chat-enabled custom tools |
POST |
/api/v1/chats/{chat_id}/abort |
Abort an active generation |
POST |
/api/v1/chats/{chat_id}/truncate/{message_id} |
Soft-delete the selected message and all later messages |
All create-chat JSON fields are optional: title defaults to New Chat;
model, reasoning_level, and enabled_tools use server-selected defaults
when omitted. Chat mutation payloads are
{"title": "..."}, {"model": "..."},
{"reasoning_level": "..."}, {"enabled_tools": ["..."]}, and
{"pinned": boolean, "archived": boolean}.
GET /api/v1/chats/search requires q (at most 200 characters) and returns
at most 50 ChatSearchResponse objects. Each contains the normal chat summary
plus one plain-text snippet and a nullable message_id. The ID identifies
the earliest matching non-deleted user, assistant, or tool message; a title-only
match has no message ID. There is no matches array or match_type field.
Whitespace-only queries return an empty list. Omitting archived searches both
active and archived chats.
Reasoning updates¶
The chat API accepts off, minimal, low, medium, high, xhigh, and
max. Values are normalized to the selected model's capabilities; none is
not a valid chat API value. The response contains the saved value, for example
{"status":"success","message":"high"}. See
Configure Reasoning Effort.
History pagination¶
GET /api/v1/chats/{chat_id}/messages accepts optional limit (1–100),
before_id, around_id, and through_id. limit counts user turns, so a page
can contain more messages than that number. before_id excludes that message
and later IDs; through_id includes messages up to that ID. around_id anchors
the page to a message in the chat.
Responses include X-History-More and X-History-Before headers. Tool outputs
are folded into their assistant tool-call entries for display. This endpoint's
representation differs from raw database records and from provider input.
Files and uploads¶
| Method | Path | Auth | Purpose |
|---|---|---|---|
POST |
/api/v1/upload?chat_id={chat_id} |
Yes | Upload a chat file as multipart field file |
GET |
/api/v1/chats/{chat_id}/files |
Yes | List files owned by the chat |
GET |
/api/v1/chats/{chat_id}/files/download/{filename} |
Yes | Download one chat file |
POST |
/api/v1/me/avatar |
Yes | Upload a profile avatar as multipart field file |
GET |
/generated-files/{chat_id}/{filename} |
Yes | Read a generated artifact owned by the chat |
Upload the file with multipart/form-data; chat_id is a required query
parameter on POST /api/v1/upload.
Upload limits are controlled by UPLOAD_MAX_UPLOAD_BYTES,
UPLOAD_MAX_AVATAR_BYTES, and UPLOAD_MAX_IMAGE_PIXELS. PDF uploads are
processed into ordered page context. Images can be used as vision inputs only
when the selected model declares image input support.
Generated tool files use the authenticated web route
/generated-files/{chat_id}/{filename} and are kept outside /static.
User and settings endpoints¶
These routes are mounted under /api/v1/me.
| Method | Path | Purpose |
|---|---|---|
GET |
/api/v1/me/ |
Return the current user profile |
PATCH |
/api/v1/me/username |
Change the username and issue refreshed auth cookies |
POST |
/api/v1/me/password |
Change the password |
DELETE |
/api/v1/me/ |
Delete your account; JSON body {"password":"..."} |
GET |
/api/v1/me/export |
Download structured account data as JSON |
GET |
/api/v1/me/settings |
Read personal settings |
POST |
/api/v1/me/settings |
Update personal_preferences |
POST /password requires current_password and new_password and
revokes existing sessions. Account export includes stored message fields and
memory data, but not credentials or binary uploads. See
Privacy and Self-Hosting.
Example settings update:
Memory endpoints¶
All memory endpoints require authentication.
| Method | Path | Purpose |
|---|---|---|
GET |
/api/v1/memories/ |
List explicit durable facts |
DELETE |
/api/v1/memories/{memory_id} |
Delete one explicit fact |
GET |
/api/v1/memories/chats/{chat_id}/summary |
Read a chat's rolling summary |
DELETE |
/api/v1/me/memory |
Clear all saved memory for the current account |
GET |
/api/v1/me/memory/profile |
Read the structured adaptive-memory profile |
PATCH |
/api/v1/me/memory/profile/section |
Replace one profile section |
DELETE |
/api/v1/me/memory/profile/section |
Clear one profile section using section query parameter |
GET |
/api/v1/me/memory/profile/history |
List profile snapshots; optional limit |
GET |
/api/v1/me/memory/profile/history/{snapshot_id} |
Read one snapshot |
POST |
/api/v1/me/memory/profile/consolidate |
Queue manual profile consolidation |
GET |
/api/v1/me/memory/model |
Read the configured memory model |
GET |
/api/v1/me/memory/observations |
List raw observations; optional consolidated filter |
PATCH /api/v1/me/memory/profile/section accepts JSON fields section and
value. The section name is one of identity,
communication_style, top_of_mind, recent_history, earlier_context, or
long_term_background; the value can contain up to 3000 characters.
Snapshot history defaults to 25 entries; the service clamps limit to 1–100.
Metadata endpoints¶
| Method | Path | Auth | Purpose |
|---|---|---|---|
GET |
/api/v1/tools |
Yes | List tools and their current schemas |
GET |
/api/v1/models |
No | List models available in the configured catalog |
GET |
/api/v1/instance |
No | Read configured public operator/privacy information |
GET |
/api/v1/favicon |
Yes | Read a citation favicon; required domain (1–2048 characters) and optional size (16–128, default 32) |
The model list is filtered by provider configuration and catalog enablement;
it is not a remote health check. /api/v1/tools returns the instance registry,
including core/custom names and groups. It does not accept a chat ID and does
not filter by one chat's active selection. The agent applies that selection
when supplying schemas and executing calls.
Admin endpoints¶
All admin endpoints require an authenticated user with is_admin=true.
| Method | Path | Purpose |
|---|---|---|
GET |
/api/v1/admin/stats |
Application statistics |
GET |
/api/v1/admin/users |
List users and usage statistics |
POST |
/api/v1/admin/users/{user_id}/reset-password |
Set a user's password |
DELETE |
/api/v1/admin/users/{user_id} |
Delete a user and owned data |
GET |
/api/v1/admin/app-settings |
Read application settings |
POST |
/api/v1/admin/app-settings |
Update application settings |
GET |
/api/v1/admin/model-settings/{name} |
Read a model override |
PUT |
/api/v1/admin/model-settings/{name} |
Update a model override |
DELETE |
/api/v1/admin/model-settings/{name} |
Reset a model override |
GET, POST |
/api/v1/admin/models |
List or add configured models |
PATCH, DELETE |
/api/v1/admin/models/{model_id} |
Edit or safely delete a model |
POST |
/api/v1/admin/models/default |
Set the enabled catalog default |
POST |
/api/v1/admin/models/import |
Merge or replace a complete JSON catalog |
GET |
/api/v1/admin/models/export |
Export portable model JSON |
POST |
/api/v1/admin/models/reload |
Reload an externally edited catalog |
GET |
/api/v1/admin/models/discovery |
Search a configured provider's models |
GET |
/api/v1/admin/models/discovery/detail |
Enrich a provider model selection |
Model-override {name} is either chat-title or memory. Override updates
and default-model updates accept {"model":"catalog-model-id"}. Catalog
imports accept {"mode":"merge","catalog":{...}}, with replace as the
other mode. Model IDs in edit/delete paths can contain /.
Discovery requires provider; it accepts query (up to 200 characters) and
limit (1–100, default 30). Detail lookup requires provider and model_id
(1–256 characters). These calls contact the configured provider and can return
502 for discovery failures.
Health endpoints¶
Health routes are intentionally unversioned:
| Method | Path | Meaning |
|---|---|---|
GET |
/health |
Basic service health |
GET |
/health/ready |
Database readiness check |
GET |
/health/live |
Process liveness check |
WebSocket protocol¶
Connect to /ws/{chat_id} for a chat owned by the authenticated user.
Use a bearer header or the access_token cookie. A supplied Origin must
match a configured trusted origin or the request's own origin. Without an
Origin, bearer-header authentication is required.
Sending a message¶
Send a JSON object in a text frame:
{
"message": "Summarize these files",
"vision_files": ["diagram.png"],
"attached_files": ["notes.pdf"],
"request_id": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"
}
Attachment filenames must already belong to that chat. The arrays and
request_id are optional. If supplied, request_id must match
[a-f0-9-]{32,36}; retain it when retrying the same submission so the server
can recognize an already-saved message. Text can be empty when permitted
attachments provide the input.
For keepalive, send {"message":"ping"} without request ID or attachments.
The response is {"type":"pong"}. A raw non-JSON ping frame is invalid.
Reconnecting to a turn¶
Both query parameters are needed to resume from a retained event boundary:
turn_id: the ID of the turn being resumed, up to 64 characters.last_event_id: the last applied event ID, a nonnegative integer.
The server sends stream_sync. Its reset flag tells the client whether to
replace the displayed stream with the supplied snapshot or apply retained
events after the requested boundary. For an active turn, it also carries
active, turn_id, event_id, request_id, before_message_id, events,
and snapshot_lost. With no active turn, it returns active: false,
reset: true, and an empty events array.
A missing/mismatched turn ID cannot resume solely from last_event_id.
Replay is bounded and in memory. After a restart, or when a snapshot is lost, reconcile with saved history rather than assuming every streamed chunk can be replayed. A browser disconnect does not itself cancel the turn.
Server events¶
Broadcast turn events include event_id and turn_id for ordering.
| Type | Main fields | Meaning |
|---|---|---|
stream_sync |
active, reset, events, IDs |
Reconnect state or snapshot |
turn_start |
before_message_id, request_id |
A generation turn began |
accepted |
request_id, message |
Submitted user message was persisted |
request_resolved |
request_id, message_id |
Retry matched an already-saved request |
rejected |
request_id, content |
Submission could not start |
status |
content |
Status such as thinking or compacting |
content |
content |
Answer text chunk |
reasoning |
content |
Returned reasoning text or summary chunk |
tool_preparing |
name, tool_call_id |
Tool call recognized before arguments complete |
tool_start |
name, input, tool_call_id |
Tool invocation announced |
tool_end |
name, output, tool_call_id |
Tool result available |
title_update |
title |
Optional title update after a successful answer |
done |
message_id |
Final assistant response persisted without more tool calls |
error |
content |
Failure, cancellation, or limit |
turn_end |
— | Generation ended, including after errors; title and memory work can follow |
turn_end is not a success signal. Connection and payload limits are controlled
by WEBSOCKET_*; agent turn budgets add further limits. Invalid JSON or request
IDs close with code 1007, binary frames with 1003, oversized payloads with 1009,
and connection capacity with 1013. An untrusted origin or inaccessible chat is
rejected with 1008; missing or expired authentication closes with 4001. An unavailable turn slot
produces a rejected event.
Error and status behavior¶
Typical responses use FastAPI's standard shape:
Ownership failures intentionally look like 404 so resources are not disclosed.
Use the generated schema on a development instance for exact response models
and validation constraints. Source schemas live in src/numi_chat/web/schemas/
and routes in src/numi_chat/web/routes/.