Skip to content

Chat API

The chat endpoints are the most useful surface for automation: send a question, and the answer comes back grounded in whatever your pipelines have indexed. Retrieval happens automatically on every message, scoped to the collections the authenticated user can access (or a narrower set you choose per chat).

Terminal window
curl -b cookies.txt -X POST $BASE/chats \
-H 'Content-Type: application/json' \
-d '{"model_name": "llama3.2", "message": "Summarize our vacation policy."}'

The response is the full conversation (ChatDetail):

{
"id": "…chat uuid…",
"title": "Vacation policy summary",
"collection_ids": [],
"messages": [
{ "id": "", "role": "user", "content": "Summarize our vacation policy.", "model_name": "llama3.2", "tokens_used": 12, "sibling_index": 0, "sibling_count": 1, "interrupted": false, "sources": [], "created_at": "" },
{ "id": "", "role": "assistant", "content": "Employees accrue 1.5 days…", "model_name": "llama3.2", "tokens_used": 148, "sibling_index": 0, "sibling_count": 1, "interrupted": false,
"sources": [
{ "source": "/Policies/vacation_policy.md", "chunk_index": 0,
"similarity_score": 0.83, "collection_name": "company_docs",
"content": "Employees accrue 1.5 days of paid leave per month…" }
],
"created_at": "" }
],
"created_at": "",
"updated_at": ""
}

Things to know:

  • This form blocks until the answer is fully generated. Set your HTTP client’s timeout generously, or use the streaming endpoints below.
  • model_name must be a model that exists in the organization, or you get 404 {"detail": "Model '…' not found"}.
  • Optional collection_ids restricts retrieval for this chat to those collections; every id must be a collection you can access (404 otherwise). Omitted or empty means “everything I can access”.
  • A short title is generated automatically from your first message.
  • Retrieval is best-effort: if nothing relevant is indexed, the model answers from general knowledge — the shape of the response is identical (with an empty sources list).

Each freshly generated assistant message carries a sources array — the retrieved chunks that actually made it into the model’s context, with the file path, chunk index, similarity score, collection name, and the chunk text itself. This is what powers the citation underlines in the chat UI.

Retrieval results are not persisted. Sources appear only on the reply you just generated; any later read of the same conversation (GET /chats/{id}/branch, switch_branch) returns those messages with sources empty. If your integration needs the citations, capture them from the generating response.

Chat can stream the reply token by token instead of blocking. The streaming endpoints return NDJSON (application/x-ndjson): one JSON object per line, not Server-Sent Events.

Terminal window
curl -N -b cookies.txt -X POST $BASE/chats/<chat_id>/followup/stream \
-H 'Content-Type: application/json' \
-d '{"model_name": "llama3.2", "message": "And how many days roll over?"}'
{"type": "user_message", "message": { …your persisted message… }}
{"type": "token", "content": "Employees"}
{"type": "token", "content": " may"}
{"type": "done", "message": { …full assistant message, with sources… }, "stats": {"eval_count": 148, "elapsed": 4.2}}

Line types:

Line Meaning
user_message Your message as persisted (first line, followup/stream only)
token The next piece of the reply — append content as it arrives
done The completed assistant message (including sources) plus generation stats
error Generation or persistence failed mid-stream — detail explains, persisted says whether a partial reply was saved

Because the HTTP status is already 200 when generation starts, mid-stream failures arrive as an error line rather than an error status — handle it. If your client disconnects mid-stream, the partial reply is saved with interrupted: true.

Starting a new chat with streaming is a two-step dance: create the chat and user message first, then stream the reply onto it:

Terminal window
# 1. Create the chat; the returned branch ends on your user message
curl -b cookies.txt -X POST $BASE/chats/init \
-H 'Content-Type: application/json' \
-d '{"model_name": "llama3.2", "message": "Summarize our vacation policy."}'
# 2. Stream the reply to that message
curl -N -b cookies.txt -X POST \
$BASE/chats/<chat_id>/messages/<user_message_id>/respond/stream \
-H 'Content-Type: application/json' \
-d '{"model_name": "llama3.2"}'

Editing works the same way: POST /chats/{chat_id}/edit_message/init with {"user_message_id": …, "new_content": …} creates the new branch, then respond/stream on the returned leaf generates the answer.

Terminal window
curl -b cookies.txt -X POST $BASE/chats/<chat_id>/followup \
-H 'Content-Type: application/json' \
-d '{"model_name": "llama3.2", "message": "And how many days roll over?"}'

Returns just the new [user, assistant] message pair. You can switch model_name on any message.

Terminal window
curl -b cookies.txt -X PUT $BASE/chats/<chat_id>/collections \
-H 'Content-Type: application/json' \
-d '{"collection_ids": ["<collection_id>", …]}'

Restricts which collections this chat searches from now on; an empty list restores “all collections I can access”. Ids you can’t access come back as a 404 naming them. The chat’s current scope is the collection_ids field on ChatDetail. If your access to a scoped collection is later revoked, it is silently dropped from retrieval rather than erroring.

Terminal window
curl -b cookies.txt $BASE/chats # summaries: id, title, timestamps
curl -b cookies.txt -X POST $BASE/chats/<chat_id> \
-H 'Content-Type: application/json' -d '{"new_title": "Policy Q&A"}'
curl -b cookies.txt -X DELETE $BASE/chats/<chat_id> # permanent

Chats are private to the authenticated user.

Conversations are trees: editing a user message or regenerating an assistant reply creates a sibling version rather than overwriting. Each message carries sibling_index / sibling_count so you can tell where alternatives exist.

Terminal window
# Re-answer an assistant message (adds a sibling)
curl -b cookies.txt -X POST $BASE/chats/<chat_id>/regenerate_response \
-H 'Content-Type: application/json' \
-d '{"assistant_message_id": "<message_id>", "model_name": "llama3.2"}'
# Edit a user message (new branch + fresh answer)
curl -b cookies.txt -X POST $BASE/chats/<chat_id>/edit_message \
-H 'Content-Type: application/json' \
-d '{"user_message_id": "<message_id>", "new_content": "…", "model_name": "llama3.2"}'
# Choose which sibling is active
curl -b cookies.txt -X POST $BASE/chats/<chat_id>/switch_branch \
-H 'Content-Type: application/json' \
-d '{"fk_message_id": "<parent_message_id>", "sibling_index": 1}'
# Read the currently active branch
curl -b cookies.txt $BASE/chats/<chat_id>/branch

All four return the full active-branch ChatDetail, so your client always has a consistent view after any mutation. Remember that branch and switch_branch are reads as far as retrieval is concerned — messages come back with sources empty.