HTTP Endpoints
REST reference for the gateway service. All application routes mount under the /api_lis prefix; only GET /health sits at the root. Every /api_lis endpoint is guarded by Authentication (X-Api-Key + X-User-ID); WebSocket relays are documented separately in the WebSocket Protocol.
Conventions
- Base path: all session and memory routes are prefixed with
/api_lis(for example/api_lis/sessions).GET /healthis the only route not under this prefix. - Auth: every
/api_lisroute depends onget_current_user, which requires a validX-User-IDUUID and — whenGATEWAY_API_TOKENis set — a matchingX-Api-Key. A missing or malformedX-User-IDreturns400; a mismatched API key returns401. - User scope:
user_idcomes from theX-User-IDheader and scopes every query. A session or fact that belongs to another user is reported as404(not found), never403. - Timestamps: all timestamps are ISO 8601 strings.
Error response shape
Handled AppError responses share one JSON body:
{
"error": "not_found",
"message": "Session not found."
}
| Status | Meaning |
|---|---|
400 | Malformed path/header value (bad UUID, missing X-User-ID) |
401 | Invalid or missing X-Api-Key (only when a key is configured) |
404 | Resource not found or not owned by the caller |
500 | Unhandled or repository error ({"error": "internal_error"}) |
Sessions
GET /api_lis/sessions
List every active session belonging to the authenticated user, keyed by session id.
Response 200 OK (SessionsResponse)
{
"sessions": {
"<session_uuid>": {
"avatar_id": "<avatar_uuid>",
"name": "My session",
"created_at": "2024-01-01T12:00:00Z"
}
}
}
POST /api_lis/sessions
Create a session linked to an avatar. The avatar_id is validated against ml.avatars; the resolved avatar UUID is stored as the session's avatar_id.
Request (CreateSessionRequest)
{
"avatar_id": "<avatar_uuid>"
}
Response 201 Created (SessionSummary)
{
"session_id": "<session_uuid>",
"name": null,
"created_at": "2024-01-01T12:00:00Z"
}
Errors: 404 if the avatar does not exist; 400 if avatar_id is not a valid UUID.
GET /api_lis/sessions/{session_id}/messages
Return conversation history for a session in chronological order. The repository fetches up to the last 1000 turns newest-first, then reverses them.
Response 200 OK (ChatMessagesResponse)
{
"session_id": "<session_uuid>",
"messages": [
{
"id": 1,
"role": "user",
"content": "Hello",
"created_at": "2024-01-01T12:00:00Z"
},
{
"id": 2,
"role": "assistant",
"content": "Hi there!",
"created_at": "2024-01-01T12:00:01Z"
}
],
"count": 2
}
The message list does not include attachments. content may be null for turns with no text body.
Errors: 404 if the session does not exist or does not belong to the user; 400 if session_id is not a valid UUID.
PATCH /api_lis/sessions/{session_id}
Update mutable session fields. Only the fields present in the request body are changed (unset fields are left untouched).
Request (PatchSessionRequest)
{
"name": "Renamed session",
"description": "Optional notes"
}
Response 204 No Content
Errors: 404 if the session does not exist or does not belong to the user; 400 if session_id is not a valid UUID.
DELETE /api_lis/sessions/{session_id}
Soft-delete a session by setting its status to deleted. The row is retained.
Response 204 No Content
Errors: 404 if the session does not exist or does not belong to the user; 400 if session_id is not a valid UUID.
Memory
The memory router is mounted with its own /memory prefix under /api_lis, so the full paths are /api_lis/memory and /api_lis/memory/{fact_id}.
GET /api_lis/memory
List the authenticated user's memory facts, ordered by last_seen_at descending. Defaults to active facts only.
Query parameters
| Param | Type | Default | Description |
|---|---|---|---|
avatar_id | uuid | — | Filter to a specific avatar |
statuses | string[] | ["active"] | Filter by status; repeatable (e.g. ?statuses=active&statuses=superseded) |
Response 200 OK (MemoryListResponse)
{
"facts": [
{
"fact_id": "<uuid>",
"avatar_id": "<uuid>",
"category": "preference",
"fact_key": "language",
"fact_value": "English",
"confidence": 0.92,
"status": "active",
"last_seen_at": "2024-01-01T12:00:00Z",
"created_at": "2024-01-01T10:00:00Z"
}
],
"total": 1
}
DELETE /api_lis/memory/{fact_id}
Soft-delete a single memory fact owned by the caller (sets status to deleted). Only the owning user can delete their own facts.
Response 204 No Content
Errors: 400 if fact_id is not a valid UUID.
There is no POST /api_lis/memory endpoint. Memory facts are created by the separate memory_service extraction pipeline, not through the gateway.
Health
GET /health
Liveness and dependency probe. Not under /api_lis and not authenticated. The gateway makes a 2-second HTTP call to the conversation engine's /health and reports ok only when the engine reports ok, otherwise degraded.
Response 200 OK (HealthResponse)
{
"status": "ok",
"service": "gateway_service",
"dependencies": {
"conversation_engine": { "status": "ok" }
}
}
Only the conversation engine is probed. STT, TTS, and the database pool are not checked by this endpoint.
WebSocket endpoints
The gateway also exposes three authenticated WebSocket relays under /api_lis: /api_lis/sessions/{session_id}/message, /api_lis/transcribe, and /api_lis/voiceover. Their frame formats, events, and close codes are covered in the WebSocket Protocol.