Chat

Conversations are trees of messages, not flat threads: every user message can carry a parent_message_id, which is what makes edit-and-resend branches possible. Sending a message doesn't return JSON — it opens a Server-Sent Events stream that carries retrieval, agent, and token frames as the answer is generated.

All routes below require a session JWT (Authorization: Bearer $TOKEN), gated per-route by the granular permission shown next to each endpoint.

Create Chat

Create a new chat in a workspace.

POST /api/v1/chats · Auth: JWT, chat.generate

Sample request

curl -sX POST https://ragz.example.com/api/v1/chats \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"workspace_id": "a1b2...-uuid", "title": "Q3 policy questions"}'

Sample response

{ "id": "c3d4...-uuid", "workspace_id": "...", "title": "...", ... }

List Chats

List your chats, optionally scoped to one workspace.

GET /api/v1/chats · Auth: JWT, chat.read

Notes

  • Accepts an optional ?workspace_id= query parameter to filter to a single workspace.

Get Chat Tree

Return the full message tree for a chat.

GET /api/v1/chats/{chat_id} · Auth: JWT, chat.read


Rename Chat

Rename a chat.

PATCH /api/v1/chats/{chat_id} · Auth: JWT, chat.update


Delete Chat

Delete a chat.

DELETE /api/v1/chats/{chat_id} · Auth: JWT, chat.delete


Send Message

Send a message on a chat. This does not return JSON — the response is a text/event-stream that carries retrieval, agent, and token frames as the answer is generated.

POST /api/v1/chats/{chat_id}/messages · Auth: JWT, chat.generate

Request body

ParameterTypeRequiredDescription
contentstringYesThe message text. 1–32000 chars.
parent_message_idstring (uuid) | nullNoOmit entirely to append to the active branch; pass null to start a new root sibling (editing a root message); pass a UUID to branch from a specific message (edit-and-resend).
model_idstring (uuid) | nullNoOptional per-message model override; omitted/null uses the workspace's default model.
reasoning_effort"off" | "low" | "medium" | "high"NoOnly valid on models that support it — a mismatch is rejected before any stream bytes are sent.
attachment_idsarrayNoUp to 50 ids from prior POST .../attachments calls to include with this turn.
web_search_consentedbooleanNoMust be explicitly true for the agent to run an external web search on this turn; fails closed even when the workspace has web search enabled.

Sample request

curl -N -sX POST https://ragz.example.com/api/v1/chats/$CHAT_ID/messages \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"content": "What is our refund policy?", "reasoning_effort": "off"}'

Sample response

The response is text/event-stream. Each frame is event: <name> + data: <json>. Frames observed over the course of an answer, roughly in order:

event: retrieval_started
data: {}
 
event: sources
data: {"sources":[{"marker":1,"document_id":"...","filename":"policy.pdf","page":4,...}]}
 
event: token
data: {"delta":"Enterprise"}
 
event: token
data: {"delta":" customers"}
 
event: citations
data: {"citations":[{"marker":1,"document_id":"...","chunk_ref":"...","page":4,"section":"Refunds","version":2,"url":null}]}
 
event: done
data: {"message_id":"...","prompt_tokens":812,"completion_tokens":96,"no_answer":false,"grounding":"documents","validation_failed":false}

Event frames

EventCarries
retrieval_startedSignals retrieval has begun (empty payload)
agent_step{n, tool, query} — one agent tool invocation (Phase 3 agent loop)
tool_result{n, tool, results} — display-only results for a tool step (e.g. web search hits)
sources{sources: [...]} — retrieved chunks backing the answer, pre-citation
token{delta} — one incremental chunk of the assistant's text
citations{citations: [...]} — the final citation list (document, chunk, page, section, version, score, url)
blocks{blocks: [...]} — generative-UI blocks (charts/tables), only when the workspace has that feature enabled
done{message_id, prompt_tokens, completion_tokens, no_answer, grounding, validation_failed}
error{detail} — a typed error mid-stream

Notes

  • done.no_answer: true means Ragz declined to answer because retrieval didn't produce sufficient grounding — this is deliberate no-answer mode, not a failure. done.grounding is "documents" or "web" depending on what backed the answer.

Rate limited

Message sends are capped per user (not just per IP) — expect a 429 if you script rapid-fire sends. Retry with backoff.


Regenerate Message

Regenerate an assistant reply — streams the identical frame set (see Send Message) for a fresh answer to an existing assistant message, optionally with a different model_id / reasoning_effort.

POST /api/v1/messages/{message_id}/regenerate · Auth: JWT, chat.generate

Request body

ParameterTypeRequiredDescription
model_idstring (uuid) | nullNoOptional per-message model override; omitted/null uses the workspace's default model.
reasoning_effort"off" | "low" | "medium" | "high"NoOnly valid on models that support it.

Event frames

Same event set as Send Message.


Set Message Feedback

Thumbs up/down + optional comment on an assistant message.

PUT /api/v1/messages/{message_id}/feedback · Auth: JWT, chat.feedback


Clear Message Feedback

Clear feedback on an assistant message.

DELETE /api/v1/messages/{message_id}/feedback · Auth: JWT, chat.feedback


Upload Attachment

Upload a file first (multipart/form-data, capped by the interactive upload limit), then reference its id in attachment_ids on the next Send Message call.

POST /api/v1/chats/{chat_id}/attachments · Auth: JWT, chat.attachments.create

Sample request

curl -sX POST https://ragz.example.com/api/v1/chats/$CHAT_ID/attachments \
  -H "Authorization: Bearer $TOKEN" \
  -F "file=@quarterly-notes.png"

Sample response

{ "id": "e5f6...-uuid", "kind": "image", "filename": "...", "mime": "image/png", "status": "processing" }

Notes

  • Image attachments on a vision-capable model are sent to the model directly; everything else (documents, or images on a non-vision model) is routed through OCR/extraction and retrieval instead.

Get Attachment Content

Stream an attachment's bytes inline (previews/thumbnails).

GET /api/v1/chats/{chat_id}/attachments/{attachment_id}/content · Auth: JWT, chat.read