DocuStore.io
API Reference

Chat API

Endpoints for conversational RAG with SSE streaming, conversation management, and message feedback.

The chat API provides a conversational RAG interface. Users ask questions and receive grounded, citation-backed answers streamed in real time via Server-Sent Events (SSE). Conversations are persisted in MongoDB for multi-turn continuity.

Create Conversation

POST /chat
Content-Type: application/json
{
  "title": "NadD inhibitor research"
}

Returns 201 Created with the conversation metadata:

{
  "conversation_id": "uuid",
  "workspace_id": "uuid",
  "owner_id": "uuid",
  "title": "NadD inhibitor research",
  "created_at": "2026-03-29T12:00:00Z",
  "is_archived": false
}

List Conversations

GET /chat?skip=0&limit=20&is_archived=false

Returns conversations for the current user, sorted by most recent activity.

ParameterTypeDefaultDescription
skipinteger0Pagination offset
limitinteger20Max results
is_archivedbooleanfalseInclude archived conversations

Get Conversation

GET /chat/{conversation_id}?skip=0&limit=100

Returns the conversation with its message history. Messages are paginated.

Send Message (SSE Stream)

POST /chat/{conversation_id}/messages
Content-Type: application/json
{
  "message": "What are the most potent NadD inhibitors in our collection?",
  "mode": "thinking"
}
FieldTypeRequiredDescription
messagestringYesUser message (1-10,000 characters)
modestringNoquick, thinking, deep_thinking, or literature (published papers instead of this corpus; only when LITERATURE_ENABLED=true, otherwise 404). Null uses server default.
reasoningobjectNoPer-lane reasoning override, e.g. {"synthesis": "high"}. Lanes: synthesis, retrieval, base; levels: off, low, medium, high. Absent lanes use the server default.
statsbooleanNoCompute chart panels for this answer (default false)

Returns a text/event-stream response. Because this is a POST, read the stream with fetch() (or an SSE library that supports POST), not the browser EventSource. Each frame carries an id: sequence number.

Generation is decoupled from the connection: if the client disconnects, the run keeps going and its answer is still saved. Only one run per conversation at a time; a second send returns 409.

Reattach and Stop

GET /chat/{conversation_id}/messages/stream?after=-1

Reattaches to an in-flight (or just-finished) run: replays frames with an id greater than after, then tails live until done. Returns 404 if there is no active run.

DELETE /chat/{conversation_id}/run

Stops an in-flight run. Returns 204 No Content; the partial answer is discarded.

SSE Event Types

Events are delivered as named SSE events with JSON data payloads:

agent_step

Emitted when a pipeline stage starts or completes (status: started, completed, or failed). Completed steps may carry an output summary, and thinking_content with a thinking_label for stages that produce reasoning traces.

event: agent_step
data: {"type": "step_completed", "step": "planning", "status": "completed", "output": "Type: factual. ...", "thinking_content": "...", "thinking_label": "Query Analysis"}

event: agent_step
data: {"type": "step_started", "step": "retrieval", "status": "started", "description": "Searching documents (agentic mode)..."}

event: agent_step
data: {"type": "step_completed", "step": "retrieval", "status": "completed", "output": "Found 12 relevant sources (840ms)"}

query_context

Emitted after query planning with extracted entities and classification.

event: query_context
data: {"type": "query_context", "query_context_entities": [{"entity_text": "NadD", "entity_type": "target"}], "query_context_authors": [], "query_context_type": "factual", "query_context_reformulated": "most potent NadD inhibitors"}

retrieval_results

Emitted after context assembly with the citation sources used for the answer.

event: retrieval_results
data: {"type": "retrieval_results", "sources": [{"citation_index": 1, "artifact_id": "...", "page_id": "...", "page_index": 7, "artifact_title": "...", "text_excerpt": "...", "similarity_score": 0.92, "source_type": "document"}]}

literature_results

Emitted in literature mode with the papers found by the literature search tool (Europe PMC), including each paper's licence and whether it can be ingested.

event: literature_results
data: {"type": "literature_results", "literature_results": [{"external_id": "...", "title": "...", "doi": "...", "year": 2024, "licence": "cc by", "is_open_access": true, "is_ingestable": true, "url": "...", "...": "..."}]}

token

Streaming answer tokens. Emitted only during the answer formatting stage.

event: token
data: {"type": "token", "delta": "The most potent"}

event: token
data: {"type": "token", "delta": " NadD inhibitor in your collection is"}

reasoning_token

Streaming reasoning tokens, emitted alongside the answer stream when the model exposes its reasoning.

event: reasoning_token
data: {"type": "reasoning_token", "delta": "The user is asking about"}

grounding_result

Emitted after inline verification.

event: grounding_result
data: {"type": "grounding_result", "grounding_is_grounded": true, "grounding_confidence": 0.92, "grounding_llm_verified": false}

structured_block

Rich content blocks such as tables, molecule renderings, or charts. The block.type is one of text, table, molecule, citation_list, source_card, or chart.

event: structured_block
data: {"type": "structured_block", "block": {"type": "molecule", "smiles": "CC(=O)Oc1ccccc1C(=O)O", "label": "SACC-111", "page_id": "..."}}

done

Final event with metadata about the completed response.

event: done
data: {"type": "done", "message_id": "uuid", "total_tokens": 2340, "prompt_tokens": 1800, "completion_tokens": 540, "model": "...", "duration_ms": 8500, "sources": [...]}

error

Emitted if the pipeline encounters an error.

event: error
data: {"type": "error", "error_message": "Failed to connect to LLM provider"}

Client Example

const res = await fetch(`${API_URL}/chat/${conversationId}/messages`, {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    Authorization: `Bearer ${idpToken}`,
    "X-Authz-Token": authzToken,
  },
  body: JSON.stringify({ message, mode: "thinking" }),
});

const reader = res.body!.pipeThrough(new TextDecoderStream()).getReader();
let buffer = "";
while (true) {
  const { value, done } = await reader.read();
  if (done) break;
  buffer += value;
  const frames = buffer.split("\n\n");
  buffer = frames.pop()!; // keep the incomplete frame
  for (const frame of frames) {
    const line = frame.split("\n").find((l) => l.startsWith("data: "));
    if (!line) continue;
    const data = JSON.parse(line.slice(6));
    if (data.type === "token") appendToAnswer(data.delta);
    if (data.type === "done") setCitations(data.sources);
  }
}

To resume after a dropped connection, read GET /chat/{conversation_id}/messages/stream?after=<last id> the same way.

Record Feedback

POST /chat/{conversation_id}/messages/{message_id}/feedback
Content-Type: application/json
{
  "feedback": "positive"
}

Returns 204 No Content. Records thumbs-up or thumbs-down feedback on a specific assistant message. Accepts "positive" or "negative".

Delete Conversation

DELETE /chat/{conversation_id}

Returns 204 No Content. Deletes the conversation and all its messages.