Documents
What is the complete API flow for building a frontend UI with RAGFlow, covering dialogs, conversations, message history, streaming responses, and deletion?
What is the complete API flow for building a frontend UI with RAGFlow, covering dialogs, conversations, message history, streaming responses, and deletion?
Type
Answer
Status
Published
Created
May 21, 2026
Updated
Jun 2, 2026
Created by
Dosu Bot
Updated by
Dosu Bot

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 conversation row. 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_id and session_id are 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, set pass_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_id or conversation_id can 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:

  • answer field is accumulated (full text so far, not a delta)
  • reference appears in later chunks once retrieval completes
  • "data": true = stream complete (final event)
  • [1] in the answer = citation marker linking to reference.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#

APIMethodPurpose
/v1/document/image/<image_id>GETFetch 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,doc2GETFetch document thumbnail previews

Error Response Format (all endpoints)#

{
  "code": 102,
  "message": "Not authorized.",
  "data": null
}

// 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:

  1. 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).
  2. Optimistic UI — Append user message immediately before sending.
  3. Streaming accumulation — Replace the assistant placeholder content on each SSE event (it's accumulated, not delta).
  4. Abort support — Store an AbortController in state; call controller.abort() on navigation.
  5. Session list caching — Cache sessions per dialog in a Map; invalidate on create/delete only.
  6. Reference rendering — Map [n] citation markers to reference.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
What is the complete API flow for building a frontend UI with RAGFlow, covering dialogs, conversations, message history, streaming responses, and deletion? | Dosu