Skip to main content

Chat API

Manage conversations and interact with AI using RAG-powered chat.

Base path: /api/v1/chats


Conversations

List Chats

GET /api/v1/chats

Returns all chats without message bodies.

Query Parameters

ParameterTypeRequiredDefaultDescription
pageintegerNo1Page number (>= 1)
page_sizeintegerNoServer defaultItems per page (>= 1, clamped to server max)
scopedbooleanNononeFilter by scope status. true returns only chats with source scoping, false returns only unscoped chats. Omit to return all chats.
qstringNononeCase-insensitive title substring filter (max 200 chars). Used by the web UI's chat-switcher search.

Response

Status: 200 OK

{
"data": [
{
"id": "abc123def456",
"title": "Research Discussion",
"status": "active",
"created_at": "2026-03-09T14:30:00.000000",
"updated_at": "2026-03-09T14:35:00.000000",
"message_count": 4,
"source_ids": ["src-uuid-1", "src-uuid-2"]
}
],
"pagination": {
"total": 1,
"page": 1,
"page_size": 50,
"total_pages": 1,
"has_next": false,
"has_prev": false
}
}

curl Example

# List all chats
curl -s http://localhost/api/v1/chats

# With pagination
curl -s "http://localhost/api/v1/chats?page=1&page_size=10"

# Only scoped chats
curl -s "http://localhost/api/v1/chats?scoped=true"

Create Chat

POST /api/v1/chats

Creates a new conversation. Optionally scope it to specific sources or tags.

Request Body

FieldTypeRequiredDescription
titlestringYesChat title
source_idsstring[]NoSource IDs to scope the chat to
tag_idsstring[]NoTag IDs (resolved to their source IDs and merged with source_ids)
{
"title": "Research Discussion",
"source_ids": ["src-uuid-1"],
"tag_ids": ["tag-uuid-1"]
}

Response

Status: 201 Created

{
"id": "abc123def456",
"title": "Research Discussion",
"status": "active",
"created_at": "2026-03-09T14:30:00.000000",
"updated_at": "2026-03-09T14:30:00.000000",
"message_count": 0,
"source_ids": ["src-uuid-1", "src-uuid-from-tag"],
"messages": []
}

curl Example

# Create a basic chat
curl -s -X POST http://localhost/api/v1/chats \
-H "Content-Type: application/json" \
-d '{"title": "Research Discussion"}'

# Create a scoped chat
curl -s -X POST http://localhost/api/v1/chats \
-H "Content-Type: application/json" \
-d '{
"title": "Scoped Discussion",
"source_ids": ["src-uuid-1"],
"tag_ids": ["tag-uuid-1"]
}'

Delete All Chats

DELETE /api/v1/chats

Deletes all chats and their messages for the current database. This operation is irreversible.

Response

Status: 204 No Content

No response body.

curl Example

curl -s -X DELETE http://localhost/api/v1/chats

Get Chat

GET /api/v1/chats/{chat_id}

Returns a single chat with all of its messages.

Path Parameters

ParameterTypeRequiredDescription
chat_idstringYesChat ID

Response

Status: 200 OK

{
"id": "abc123def456",
"title": "Research Discussion",
"status": "active",
"created_at": "2026-03-09T14:30:00.000000",
"updated_at": "2026-03-09T14:35:00.000000",
"message_count": 2,
"source_ids": ["src-uuid-1"],
"messages": [
{
"id": "msg-uuid-1",
"role": "user",
"content": "What are the key findings?",
"timestamp": "2026-03-09T14:30:05.000000",
"extra_metadata": null
},
{
"id": "msg-uuid-2",
"role": "assistant",
"content": "Based on the documents, the key findings are...",
"timestamp": "2026-03-09T14:30:12.000000",
"extra_metadata": null
}
]
}

curl Example

curl -s http://localhost/api/v1/chats/abc123def456

Update Chat Title

PATCH /api/v1/chats/{chat_id}

Updates the title of a chat.

Path Parameters

ParameterTypeRequiredDescription
chat_idstringYesChat ID

Request Body

FieldTypeRequiredDescription
titlestringYesNew chat title
{
"title": "Updated Title"
}

Response

Status: 200 OK

Returns the full chat object with updated title. See Get Chat for the response schema.

curl Example

curl -s -X PATCH http://localhost/api/v1/chats/abc123def456 \
-H "Content-Type: application/json" \
-d '{"title": "Updated Title"}'

Update Chat Status

PATCH /api/v1/chats/{chat_id}/status

Updates the status of a chat.

Path Parameters

ParameterTypeRequiredDescription
chat_idstringYesChat ID

Request Body

FieldTypeRequiredDescription
statusstringYesNew status. Valid values: active, processing, completed, error
{
"status": "completed"
}

Response

Status: 200 OK

Returns the full chat object with updated status. See Get Chat for the response schema.

curl Example

curl -s -X PATCH http://localhost/api/v1/chats/abc123def456/status \
-H "Content-Type: application/json" \
-d '{"status": "completed"}'

Delete Chat

DELETE /api/v1/chats/{chat_id}

Deletes a chat and all its messages.

Path Parameters

ParameterTypeRequiredDescription
chat_idstringYesChat ID

Response

Status: 204 No Content

No response body.

curl Example

curl -s -X DELETE http://localhost/api/v1/chats/abc123def456

Chat Count

GET /api/v1/chats/stats/count

Returns the total number of chats.

Response

Status: 200 OK

{
"count": 42
}

curl Example

curl -s http://localhost/api/v1/chats/stats/count

Messages

Add Message

POST /api/v1/chats/{chat_id}/messages

Adds a message to an existing chat.

Path Parameters

ParameterTypeRequiredDescription
chat_idstringYesChat ID

Request Body

FieldTypeRequiredDescription
rolestringYesMessage role (e.g. user, assistant, system)
contentstringYesMessage content
extra_metadataobjectNoAdditional metadata to attach to the message
{
"role": "user",
"content": "What are the key findings?",
"extra_metadata": null
}

Response

Status: 201 Created

{
"id": "msg-uuid-1",
"role": "user",
"content": "What are the key findings?",
"timestamp": "2026-03-09T14:30:05.000000",
"extra_metadata": null
}

curl Example

curl -s -X POST http://localhost/api/v1/chats/abc123def456/messages \
-H "Content-Type: application/json" \
-d '{"role": "user", "content": "What are the key findings?"}'

List Messages

GET /api/v1/chats/{chat_id}/messages

Returns all messages for a chat in chronological order (oldest first).

Path Parameters

ParameterTypeRequiredDescription
chat_idstringYesChat ID

Response

Status: 200 OK

[
{
"id": "msg-uuid-1",
"role": "user",
"content": "What are the key findings?",
"timestamp": "2026-03-09T14:30:05.000000",
"extra_metadata": null
},
{
"id": "msg-uuid-2",
"role": "assistant",
"content": "Based on the documents, the key findings are...",
"timestamp": "2026-03-09T14:30:12.000000",
"extra_metadata": {
"model": "gpt-4",
"tokens_used": 350
}
}
]

curl Example

curl -s http://localhost/api/v1/chats/abc123def456/messages

Tool Approval

Approve or Reject a Tool Call

POST /api/v1/chats/{chat_id}/tool_decision

Resolves a pending tool-call approval. When the AI requests a tool that requires user approval (per the chat.tool_approval setting), the chat worker emits a tool_approval_required event on GET /chats/{chat_id}/events and pauses. This endpoint records your decision; the worker picks it up and either runs the tool or tells the model the call was denied. An unanswered request is automatically denied after chat.tool_approval_timeout_seconds (default 120s).

Path Parameters

ParameterTypeRequiredDescription
chat_idstringYesChat ID

Request Body

FieldTypeRequiredDescription
tool_call_idstringYesLLM-assigned tool_call_id from the tool_approval_required SSE event
decisionstringYesUser's decision: "approve" or "reject"
{
"tool_call_id": "call_abc123",
"decision": "approve"
}

Response

Status: 204 No Content

No response body on success. Returns 404 Not Found if no pending approval matches the given tool_call_id for that chat.

curl Example

curl -s -X POST http://localhost/api/v1/chats/abc123def456/tool_decision \
-H "Content-Type: application/json" \
-d '{"tool_call_id": "call_abc123", "decision": "approve"}'

Stop an In-Flight Response

POST /api/v1/chats/{chat_id}/cancel

Requests cancellation of the chat's running background turn. The worker stops at the next step boundary (between tool executions or before the next LLM call), keeps everything gathered so far, persists the partial answer with a "stopped at your request" notice, and publishes a done event with status: "cancelled" on GET /chats/{chat_id}/events. The chat returns to active and is immediately usable again.

A turn that is mid-stream on a single long LLM response finishes that stream first — cancellation lands at step boundaries, not mid-token.

Path Parameters

ParameterTypeRequiredDescription
chat_idstringYesChat ID

Response

Status: 202 Accepted

{
"status": "cancelling"
}

Returns 404 Not Found for an unknown chat, 409 Conflict when the chat has no turn in progress, and 503 Service Unavailable when the cancellation transport (Valkey) is unavailable.

curl Example

curl -s -X POST http://localhost/api/v1/chats/abc123def456/cancel

Retry a Failed Turn

POST /api/v1/chats/{chat_id}/retry

Re-runs the chat's last turn after a worker failure without adding a new user message. A failed run persists nothing, so the conversation history already ends with your message — retrying re-enqueues the background turn from that history. (Re-POSTing through /send would duplicate the user message.)

Path Parameters

ParameterTypeRequiredDescription
chat_idstringYesChat ID

Response

Status: 202 Accepted

{
"task_id": "task-abc123",
"status": "processing"
}

Returns 404 Not Found for an unknown chat, and 409 Conflict when a turn is already in progress or the history contains no user message to answer. Like /send, this endpoint runs the LLM-readiness gate: 409 LLM_NOT_VERIFIED when the configured provider has not been verified (Settings → LLM → Test), or 409 EXTRACTION_MODEL_MISSING when configured Ollama model(s) are not pulled (details.missing_models lists them).

curl Example

curl -s -X POST http://localhost/api/v1/chats/abc123def456/retry

Regenerate the Last Answer

POST /api/v1/chats/{chat_id}/regenerate

Drops everything after the last user message (the old answer and its tool results) and re-runs the turn from the remaining history. Returns 202 with the new task id; 404 for an unknown chat; 409 when a turn is already processing or there is no user message to answer. Like /send, this endpoint runs the LLM-readiness gate: 409 LLM_NOT_VERIFIED when the configured provider has not been verified, or 409 EXTRACTION_MODEL_MISSING when configured Ollama model(s) are not pulled (details.missing_models lists them).

curl -s -X POST http://localhost/api/v1/chats/abc123def456/regenerate

Export a Conversation

GET /api/v1/chats/{chat_id}/export?format=json|markdown

format=json (default) returns {"data": <full chat object>}. format=markdown returns a text/markdown attachment with role headings, entity markers reduced to bold labels, and citations rendered as numbered footnotes carrying the source filename and sentence text.

curl -s "http://localhost/api/v1/chats/abc123def456/export?format=markdown" -o chat.md

Generate Title

POST /api/v1/chats/{chat_id}/generate_title

Auto-generates a short (3-6 word) title for the chat based on the first user message, using a lightweight LLM call. If no user message exists or generation fails, the chat is returned unchanged.

Path Parameters

ParameterTypeRequiredDescription
chat_idstringYesChat ID

Response

Status: 200 OK

Returns the full chat object with the generated title. See Get Chat for the response schema.

curl Example

curl -s -X POST http://localhost/api/v1/chats/abc123def456/generate_title

Scoping

Source scoping restricts which documents the AI searches during RAG. When a chat is scoped, only the specified sources are used for context retrieval. When unscoped, all enabled sources are searched.

Update Scope

PATCH /api/v1/chats/{chat_id}/scope

Updates the source scope of a chat. Source IDs from source_ids and resolved from tag_ids are merged with deduplication. A system message is injected noting the scope change.

Path Parameters

ParameterTypeRequiredDescription
chat_idstringYesChat ID

Request Body

FieldTypeRequiredDescription
source_idsstring[]NoSource IDs to scope to
tag_idsstring[]NoTag IDs (resolved to source IDs and merged)
note

At least one of source_ids or tag_ids should be provided for the scope to have an effect. If both are empty or null, the scope is effectively cleared.

{
"source_ids": ["src-uuid-1"],
"tag_ids": ["tag-uuid-1"]
}

Response

Status: 200 OK

Returns the full chat object after scope update, including the injected system message. See Get Chat for the response schema. The source_ids field reflects the new scope, and messages includes the injected system message noting the change.

curl Example

curl -s -X PATCH http://localhost/api/v1/chats/abc123def456/scope \
-H "Content-Type: application/json" \
-d '{"source_ids": ["src-uuid-1"], "tag_ids": ["tag-uuid-1"]}'

Clear Scope

DELETE /api/v1/chats/{chat_id}/scope

Removes source scoping from a chat. The AI will search all enabled sources. A system message is injected noting the scope removal.

Path Parameters

ParameterTypeRequiredDescription
chat_idstringYesChat ID

Response

Status: 200 OK

Returns the full chat object after scope removal, including the injected system message. See Get Chat for the response schema. The source_ids field is null and messages includes the injected system message noting the removal.

curl Example

curl -s -X DELETE http://localhost/api/v1/chats/abc123def456/scope

Background Processing

POST /send enqueues a message for durable background processing. GET /events subscribes to the resulting SSE stream. The worker keeps running even if the client closes the connection, and the client can reattach by opening a new events subscription — the finished answer is always persisted to the conversation.

Send Message (Background)

POST /api/v1/chats/{chat_id}/send

Saves the user message, sets the chat status to processing, and enqueues the AI completion task on the LLM queue. Returns immediately with a task ID; use Subscribe to Chat Events to observe progress.

Path Parameters

ParameterTypeRequiredDescription
chat_idstringYesChat ID

Request Body

FieldTypeRequiredDescription
contentstringYesUser message text (max 500,000 characters)
replace_from_message_idstringNoEdit-and-resend: id of an existing user message to replace. The conversation is truncated from that message (inclusive) before this content is added, atomically. 409 when the id isn't a user message in this chat.
{
"content": "What are the key findings from the uploaded paper?"
}

Response

Status: 202 Accepted

{
"task_id": "task-uuid-1",
"status": "processing"
}
FieldTypeDescription
task_idstringUnique identifier for the queued task
statusstringAlways "processing" on acceptance

Errors

StatuserrorCondition
404NOT_FOUNDUnknown chat
409LLM_NOT_VERIFIEDThe configured LLM provider has not been verified — open Settings → LLM and click Test, then retry
409EXTRACTION_MODEL_MISSINGConfigured Ollama model(s) are not pulled; details.missing_models lists them

curl Example

curl -s -X POST http://localhost/api/v1/chats/abc123def456/send \
-H "Content-Type: application/json" \
-d '{"content": "What are the key findings?"}'

Subscribe to Chat Events

GET /api/v1/chats/{chat_id}/events

Opens a reconnectable Server-Sent Events stream that delivers processing events for a background chat session. On connect, the current chat status is checked:

  • If the chat is already active or completed, a done event is emitted immediately and the stream closes.
  • If the chat is in error state, an error event is emitted and the stream closes.
  • Otherwise, the endpoint subscribes to Valkey pub/sub and forwards events until a done or error event is received, or the client disconnects.

Path Parameters

ParameterTypeRequiredDescription
chat_idstringYesChat ID

Response

Status: 200 OK Content-Type: text/event-stream

Event Types

EventDescription
contentLLM response content delta and accumulated text
thinking_deltaAI reasoning steps (when thinking is enabled)
thinkingComplete thinking block emitted after a reasoning phase (field: thinking)
timing_updateThinking-phase timing payload; fields are open-ended for forward compatibility
context_infoContext-window usage for the turn
iteration_progressA new tool-calling round started
tool_callsTools the AI wants to invoke this round
cached_tool_callsDuplicate tool calls skipped (already executed this turn)
tool_startTool execution started
tool_resultTool execution completed (includes duration_ms)
tool_approval_requiredA tool call is paused awaiting the user's decision (see Tool Approval)
tool_rejectedA gated tool call was denied (rejection or timeout)
warningNon-fatal notice (kind: output_truncated, context_overflow, spend_cap, cancelled, ...)
doneProcessing finished (final content, citations, entity references, validation). status is "completed" normally, "cancelled" when the user stopped the turn — the partial answer is persisted either way
errorProcessing failed or stream error

SSE Event Data Examples

content event:

data: {"type": "content", "delta": "Based on", "accumulated": "Based on"}

thinking event (the complete reasoning block, emitted once after a reasoning phase; thinking_delta carries the same text incrementally while the phase is running):

data: {"type": "thinking", "thinking": "The question concerns the uploaded paper, so I should search the graph first..."}

timing_update event (thinking-phase timing; the timing fields are intentionally open-ended, so treat unknown keys as forward-compatible):

data: {"type": "timing_update", "total_ms": 500}

done event:

data: {"type": "done", "status": "active"}

error event:

data: {"type": "error", "error": "Chat processing failed. Please try again.", "error_code": "CHAT_PROCESSING_FAILED"}

curl Example

curl -s -N http://localhost/api/v1/chats/abc123def456/events \
--no-buffer

Schema Introspection

SSE Event Schema Anchor

GET /api/v1/chats/_schema/sse_event

Schema-only endpoint — do not call at runtime. Its sole purpose is to force FastAPI to register ChatSSEEvent and all 15 discriminated-union variant models as named #/components/schemas entries in /openapi.json, enabling TypeScript codegen to produce a typed discriminated union for SSE event handling.

This endpoint always returns 501 Not Implemented if invoked directly. It appears in the OpenAPI schema so that code generators can reference the ChatSSEEnvelope type.

Response

Status: 501 Not Implemented (if called at runtime)

curl Example

# Do not call this endpoint in production code.
# It exists solely for OpenAPI schema generation.
curl -s http://localhost/api/v1/chats/_schema/sse_event

Response Models Reference

ChatResponse

Returned by endpoints that operate on a single chat (create, get, update, generate title, scope operations).

FieldTypeDescription
idstringUnique chat identifier
titlestringChat title
statusstringChat status (active, processing, completed, error)
created_atdatetimeCreation timestamp
updated_atdatetimeLast update timestamp
message_countintegerTotal number of messages
source_idsstring[] or nullSource IDs the chat is scoped to, or null if unscoped
messagesChatMessageResponse[]List of messages (empty in some endpoints)

ChatListResponse

Used within PaginatedChatsResponse for list operations. Same as ChatResponse but without the messages field.

FieldTypeDescription
idstringUnique chat identifier
titlestringChat title
statusstringChat status
created_atdatetimeCreation timestamp
updated_atdatetimeLast update timestamp
message_countintegerTotal number of messages
source_idsstring[] or nullSource IDs the chat is scoped to

PaginatedChatsResponse

Paginated response for listing chats.

FieldTypeDescription
dataChatListResponse[]List of chats (without messages)
paginationobjectPagination metadata (total, page, page_size, total_pages, has_next, has_prev)

ChatMessageResponse

Represents a single message within a chat.

FieldTypeDescription
idstringUnique message identifier
rolestringMessage role (user, assistant, system)
contentstringMessage content
timestampdatetimeMessage timestamp
extra_metadataobject or nullAdditional metadata (model info, token counts, etc.)

ChatCountResponse

Returned by the chat count endpoint.

FieldTypeDescription
countintegerTotal number of chats

ChatSendResponse

Returned by the send-message endpoint.

FieldTypeDescription
task_idstringUnique identifier for the queued background task
statusstringAlways "processing" when the task is accepted