RAGFlow Complete Frontend API Flow#
Database / Entity Structure#
Dialog (table: dialog)
└── Conversation (table: conversation) [FK: dialog_id → dialog.id]
└── Messages (stored as JSON array in conversation.message field)
├── user messages
└── assistant responses
└── References (stored as JSON array in conversation.reference field)
Messages are not stored in a separate table — they are a JSON array inside the
conversationrow. The backend appends to this array after each exchange.
Authentication#
All requests require:
Authorization: Bearer <accessToken>
userId and tenantId are extracted from the JWT automatically — no need to pass them as separate headers.
FLOW 1 — Load All Dialogs#
GET /api/v1/chats
GET /api/v1/chats?page=1&page_size=100&orderby=update_time&desc=true
Authorization: Bearer <accessToken>
Response:
{
"code": 0,
"data": {
"chats": [
{
"id": "abc123def456",
"name": "Product Q&A Bot",
"description": "Answers product questions",
"llm_id": "gpt-4o",
"prompt_config": {
"system": "You are a helpful assistant...",
"prologue": "Hi! How can I help you?"
},
"kb_ids": ["kb_001"],
"status": "1",
"create_time": 1716307200000,
"update_time": 1716393600000
}
],
"total": 5
},
"message": "success"
}
id= dialog_id (used in all subsequent requests)name= shown in the dropdown- Select the first (most recently updated) dialog by default
FLOW 2 & 3 — Load All Conversations for a Dialog#
GET /api/v1/chats/{chat_id}/sessions
GET /api/v1/chats/abc123def456/sessions?page=1&page_size=50&orderby=update_time&desc=true
Authorization: Bearer <accessToken>
Response (includes full message history per session):
{
"code": 0,
"data": {
"sessions": [
{
"id": "sess_001",
"name": "Chat about pricing",
"messages": [
{"role": "assistant", "content": "Hi! How can I help you?", "id": "msg_init"},
{"role": "user", "content": "What are the pricing plans?", "id": "msg_002"},
{"role": "assistant", "content": "We offer three plans...[1]", "id": "msg_003"}
],
"reference": [
{},
{
"chunks": [{"content": "...", "document_name": "pricing.pdf", "similarity": 0.92}],
"doc_aggs": [{"doc_id": "doc_pricing", "doc_name": "pricing.pdf", "count": 2}]
}
],
"create_time": 1716307200000,
"update_time": 1716307265000
}
],
"total": 12
},
"message": "success"
}
The reference array correlates by index with assistant responses (reference[0] = empty for the initial greeting, reference[1] = chunks used for the first AI answer, etc.).
FLOW 4 — Open One Conversation (Full History)#
GET /api/v1/chats/{chat_id}/sessions/{session_id}
GET /api/v1/chats/abc123def456/sessions/sess_001
Authorization: Bearer <accessToken>
Returns the same structure as above but for a single session.
FLOW 5 — Create New Conversation#
POST /api/v1/chats/{chat_id}/sessions
POST /api/v1/chats/abc123def456/sessions
Authorization: Bearer <accessToken>
Content-Type: application/json
Payload:
{
"name": "New Chat"
}
Response:
{
"code": 0,
"data": {
"id": "sess_new_002",
"name": "New Chat",
"messages": [
{"role": "assistant", "content": "Hi! How can I help you?", "id": "msg_init_002"}
],
"reference": [],
"create_time": 1716393600000,
"update_time": 1716393600000
},
"message": "success"
}
The initial greeting comes from the dialog's prompt_config.prologue.
FLOW 6 — Send User Message#
POST /api/v1/chat/completions
POST /api/v1/chat/completions
Authorization: Bearer <accessToken>
Content-Type: application/json
💡 Default behavior: When
chat_idandsession_idare provided, you only need to send the latest message. The backend automatically loads stored conversation history from the database. To override this and send full conversation history instead, setpass_all_history_messages: true.
Payload — First message (default behavior):
{
"chat_id": "abc123def456",
"session_id": "sess_new_002",
"messages": [
{"role": "user", "content": "What is RAGFlow?"}
],
"stream": true,
"quote": true
}
Note: Either
session_idorconversation_idcan be used interchangeably to identify the session/conversation.
Payload — Second message (default behavior):
{
"chat_id": "abc123def456",
"session_id": "sess_new_002",
"messages": [
{"role": "user", "content": "How do I install it?"}
],
"stream": true,
"quote": true
}
Alternative — Using question field:
{
"chat_id": "abc123def456",
"session_id": "sess_new_002",
"question": "How do I install it?",
"stream": true,
"quote": true
}
Optional — Send full conversation history:
{
"chat_id": "abc123def456",
"session_id": "sess_new_002",
"pass_all_history_messages": true,
"messages": [
{"role": "assistant", "content": "Hi! How can I help you?"},
{"role": "user", "content": "What is RAGFlow?"},
{"role": "assistant", "content": "RAGFlow is an open-source RAG engine...[1]"},
{"role": "user", "content": "How do I install it?"}
],
"stream": true,
"quote": true
}
After completion, the backend automatically persists both the user message and AI response to the session's message JSON field in the database. The frontend does NOT need to call a separate "save message" API.
FLOW 7 — Streaming Response (SSE)#
Response Headers:
Content-Type: text/event-stream; charset=utf-8
Cache-Control: no-cache
Connection: keep-alive
SSE Event Stream:
data: {"code":0,"message":"","data":{"answer":"RAGFlow","reference":{},"session_id":"sess_new_002","message_id":"msg_resp_001"}}
data: {"code":0,"message":"","data":{"answer":"RAGFlow is an open-source RAG engine based on deep document understanding.[1]","reference":{"chunks":[{"id":"c1","content":"RAGFlow is...","document_name":"readme.md","similarity":0.93}],"doc_aggs":[{"doc_id":"d1","doc_name":"readme.md","count":2}]},"session_id":"sess_new_002","message_id":"msg_resp_001"}}
data: {"code":0,"message":"","data":true}
Key points:
answerfield is accumulated (full text so far, not a delta)referenceappears in later chunks once retrieval completes"data": true= stream complete (final event)[1]in the answer = citation marker linking toreference.chunks[0]
FLOW 9 — Delete Conversation#
DELETE /api/v1/chats/{chat_id}/sessions
DELETE /api/v1/chats/abc123def456/sessions
Authorization: Bearer <accessToken>
Content-Type: application/json
Payload:
{
"ids": ["sess_001", "sess_002"]
}
Response:
{
"code": 0,
"data": true,
"message": "success"
}
This is a hard delete — the session row is permanently removed from the database.
Supporting APIs for Rendering#
| API | Method | Purpose |
|---|---|---|
/v1/document/image/<image_id> | GET | Fetch chunk image (image_id = {dataset_id}-{thumbnail_object_key}, split on first hyphen only; object key may contain hyphens like page-1.png) |
/v1/document/thumbnails?doc_ids=doc1,doc2 | GET | Fetch document thumbnail previews |
Error Response Format (all endpoints)#
{
"code": 102,
"message": "Not authorized.",
"data": null
}
Recommended Frontend Architecture (React + Jotai)#
// Atoms
const dialogsAtom = atom<IDialog[]>([]);
const selectedDialogIdAtom = atom<string | null>(null);
const sessionsAtom = atom<ISession[]>([]);
const selectedSessionIdAtom = atom<string | null>(null);
const derivedMessagesAtom = atom<IMessage[]>([]); // local, sent with each completion
const isStreamingAtom = atom(false);
const streamingAnswerAtom = atom('');
const abortControllerAtom = atom<AbortController | null>(null);
Key recommendations:
- Message state is local — Load from API on session select, maintain locally during chat, send only the latest message on each completion (unless using
pass_all_history_messages: true). - Optimistic UI — Append user message immediately before sending.
- Streaming accumulation — Replace the assistant placeholder content on each SSE event (it's accumulated, not delta).
- Abort support — Store an
AbortControllerin state; callcontroller.abort()on navigation. - Session list caching — Cache sessions per dialog in a Map; invalidate on create/delete only.
- Reference rendering — Map
[n]citation markers toreference.chunks[n-1]; show as clickable popovers.
Complete End-to-End Flow Summary#
1. GET /api/v1/chats → Load dialogs, select first (id: "dialog_A")
2. GET /api/v1/chats/dialog_A/sessions → Load conversation list for sidebar
3. POST /api/v1/chats/dialog_A/sessions {"name": "New Chat"} → Create session (id: "sess_X")
4. POST /api/v1/chat/completions {chat_id, session_id, messages: [user_q1], stream:true} → SSE stream
5. Stream ends → Backend auto-persists → Local state: [greeting, user_q1, assistant_a1]
6. POST /api/v1/chat/completions {chat_id, session_id, messages: [user_q2], stream:true} → context-aware response (server loads history)
7. User returns later → GET /api/v1/chats/dialog_A/sessions → session messages already persisted → resume chat