Chat

Ask questions answered from the collection, with cited sources.

Base URL https://futuresmart.ai/document-intelligence/api · every request sends x-api-key. Samples read the key from DOCUMENT_INTELLIGENCE_API_KEY. All endpoints

Ask a question

POST/v1/{knowledge_id}/chat

Chat over a knowledge base's indexed documents.

Runs chat/graph.py's query_refiner -> rag graph. Persists the turn to knowledge_chat_sessions/knowledge_chat_logs (session ownership: this service, per the plan's "Who calls this endpoint" §3, Option (a)). Streams via SSE when request.stream is true (default); returns a single JSON ChatResponse otherwise. 404s if knowledge_chat_config.chat_enabled is explicitly false for this knowledge_id.

Credential Document Intelligence API key (x-api-key)

Path parameters

NameTypeDescription
knowledge_idrequiredstring

Request body · application/json

NameTypeDescription
queryrequiredstringUser's chat query. Already description-enriched if an image was sent to the caller — vision handling stays upstream, see the plan's Vision section.
owner_idstring | nullOwner of the knowledge base, for owner LLM credentials and usage attribution. Resolved from knowledge_config when omitted.
session_idstring | nullChat session id. Omit to start a new session (a fresh id is minted and returned).
user_idstring | nullActing user, for audit. Distinct from owner_id. Resolved from the caller's session when omitted.
streambooleanStream the answer via SSE. When false, the full response is returned as one JSON object.Default: true
has_attachmentbooleanWhether an image was attached to this turn upstream. The query above is already description-enriched — this is a transcript-completeness flag only.Default: false
attachment_urlstring | nullURL of the attached image, if any, for transcript/audit purposes only.
from_widgetbooleanSet by the website chat bubble. Records the session's origin as "widget" even when the visitor is signed in — a credential-less caller is already detectable server-side, a signed-in one is not, so only the embed can tell us. See SessionSummary.source.Default: false
from_full_screenbooleanSet by the full-screen chat embed. For a credential-less caller, gates the request against fullscreen_is_public + fullscreen_allowed_domains instead of the widget's is_public + allowed_domains.Default: false
providerstring | nullLLM provider override applied to every node. Falls through to knowledge_chat_config / env / hardcoded default when omitted. Prefer node_configs when nodes differ.
modelstring | nullLLM model override applied to every node, e.g. 'gpt-5.4-mini'.
temperaturenumber | nullTemperature override applied to every node. Accepted but not yet applied downstream (see NodeConfig.temperature).
max_tokensinteger | nullMax output tokens override. Only meaningful for the generate_answer node; see NodeConfig.max_tokens.
system_promptstring | nullSystem prompt suffix applied to the RAG answer. Equivalent to node_configs.rag_agent.prompt; node_configs wins if both are sent.
node_configsobject | nullPer-node LLM config, keyed by node name. Accepted keys: 'rag_agent' (the retrieval ReAct loop), 'generate_answer' (the answer writer), 'refined_query_node' (the query rewriter). Any other key is a 422 — see SUPPORTED_NODES for why the Orchestrator's 'supervisor'/'response_node' have no counterpart here. Takes precedence over the flat provider/model/temperature/system_prompt fields for the nodes it names; unnamed nodes fall back to those flat fields, then to the DB row / env / default.

Other responses

  • 422 Validation Error
import os
import requests

API_ROOT = "https://futuresmart.ai/document-intelligence/api"
API_KEY = os.environ["DOCUMENT_INTELLIGENCE_API_KEY"]

r = requests.post(
    f"{API_ROOT}/v1/YOUR_COLLECTION_ID/chat",
    headers={"x-api-key": API_KEY},
    json={
        "node_configs": {
            "generate_answer": {
                "model": "gemini-2.5-flash",
                "prompt": "Answer only from the retrieved context. Include any ClickUp URLs found.",
                "provider": "google",
            },
            "rag_agent": {
                "model": "gpt-5.4-mini",
                "prompt": "Prioritise accuracy and completeness in retrieval.",
                "provider": "openai",
            },
            "refined_query_node": {
                "model": "gpt-4o-mini",
                "provider": "openai",
            },
        },
        "owner_id": "owner-123",
        "query": "What invoices do we have from March?",
        "stream": True,
        "user_id": "user-456",
    },
    timeout=120,
)
r.raise_for_status()
print(r.json())

Example request body

JSON
{
  "node_configs": {
    "generate_answer": {
      "model": "gemini-2.5-flash",
      "prompt": "Answer only from the retrieved context. Include any ClickUp URLs found.",
      "provider": "google"
    },
    "rag_agent": {
      "model": "gpt-5.4-mini",
      "prompt": "Prioritise accuracy and completeness in retrieval.",
      "provider": "openai"
    },
    "refined_query_node": {
      "model": "gpt-4o-mini",
      "provider": "openai"
    }
  },
  "owner_id": "owner-123",
  "query": "What invoices do we have from March?",
  "stream": true,
  "user_id": "user-456"
}

List conversations

GET/v1/{knowledge_id}/sessions

List a knowledge base's chat sessions, most recently active first.

Powers two screens. The Conversations rail (the owner's own threads): search, date-range filter, per-session exchange counts. And Share -> Activity, which asks the opposite question — what other people did with a shared collection — via source/exclude_user_id.

Every filter and the sort are applied in SQL, so one tab is one query: total_count is that tab's real total and a page is a page of the filtered result. (Before this, a caller had to split and re-count a page in the browser, which meant a page of 20 could render 3 rows and the counts described the page rather than the collection.)

Credential Document Intelligence API key (x-api-key)

Path parameters

NameTypeDescription
knowledge_idrequiredstring

Query parameters

NameTypeDescription
pageintegerDefault: 1
page_sizeintegerDefault: 20
fromstring (date-time) | nullISO 8601. Filter to sessions created on/after this time.
tostring (date-time) | nullISO 8601. Filter to sessions created on/before this time.
qstring | nullCase-insensitive substring match against session_title and every turn's query/response text.
user_idstring | nullFilter to sessions started by this user.
sourcestring | nullFilter by origin: "widget", "dashboard", "api", or "shared" (a dashboard conversation held by somebody other than this collection's current owner). A session recorded before migration 044 has no origin and matches none of these; it still appears when the filter is omitted.
exclude_user_idstring | nullDrop this person's own conversations. Matched case-insensitively against both user_id and user_email, so either an account id or an email works.
sortstring | nullSort key: "last_activity" (default), "questions", "title", "user". An unrecognised value falls back to the default.
orderstring"asc" or "desc" (default).Default: desc

Response · 200

NameTypeDescription
sessionsrequiredarray<SessionSummary>
↳ session_idrequiredstring
↳ titlestring | nullsession_title if ever set, else the session's first query, truncated to 120 chars.
↳ exchange_countrequiredinteger
↳ user_idstring | null
↳ user_emailstring | nullThe caller's address, captured from their session when the conversation started (migration 044). Null for a public/widget visitor, and for any session recorded before that migration — this service cannot resolve an account id to an address after the fact.
↳ user_namestring | nullDisplay name from the same session, when the identity provider supplied one. Null otherwise — fall back to user_email, not to the id.
↳ sourcestring | nullWhere the conversation came from: "widget" (website bubble or full-screen embed), "dashboard" (signed in at a screen), or "api" (master-key integration). Null for sessions recorded before migration 044. Owner-vs-invited is NOT encoded here — use ?source=shared, which derives it from the collection's current owner.
↳ created_atstring (date-time) | null
↳ updated_atstring (date-time) | nullThe most recent turn's timestamp. Null only for a session with no turns yet.
total_countrequiredinteger
current_pagerequiredinteger
total_pagesrequiredinteger

Other responses

  • 422 Validation Error
import os
import requests

API_ROOT = "https://futuresmart.ai/document-intelligence/api"
API_KEY = os.environ["DOCUMENT_INTELLIGENCE_API_KEY"]

r = requests.get(
    f"{API_ROOT}/v1/YOUR_COLLECTION_ID/sessions",
    headers={"x-api-key": API_KEY},
    timeout=120,
)
r.raise_for_status()
print(r.json())

Response shape (200)

JSON
{
  "sessions": [
    {
      "session_id": "<session_id>",
      "exchange_count": 0
    }
  ],
  "total_count": 0,
  "current_page": 0,
  "total_pages": 0
}

Get a conversation

GET/v1/{knowledge_id}/sessions/{session_id}

Reopen one session's full transcript, oldest turn first, numbered.

404s for an unknown session_id, a session_id belonging to a different knowledge_id, or a soft-deleted session — all three are "not found" from the caller's side, and get_session_transcript already can't tell them apart from a genuine miss without leaking which knowledge_id a foreign session_id belongs to.

show_sources=False strips relevant_sources/summaries/detailed_chunks from every assistant message here, same as the live /chat response — the DB row (get_session_transcript) always returns the full stored evidence; this endpoint is where that policy is applied for reads, mirroring _build_response_payload's client-facing strip.

Credential Document Intelligence API key (x-api-key)

Path parameters

NameTypeDescription
knowledge_idrequiredstring
session_idrequiredstringSession ID

Response · 200

NameTypeDescription
session_idrequiredstring
knowledge_idrequiredstring
user_idstring | null
user_emailstring | null
user_namestring | null
sourcestring | null
titlestring | null
created_atstring (date-time) | null
updated_atstring (date-time) | null
exchange_countrequiredinteger
messagesrequiredarray<ChatMessage>
↳ nrequiredinteger1-indexed position of the exchange this message belongs to.
↳ chat_idrequiredstring
↳ rolerequiredstring"user" or "assistant"
↳ messagerequiredstring
↳ created_atstring (date-time) | null
↳ is_answeredboolean | null
↳ refined_querystring | null
↳ llm_providerstring | null
↳ modelstring | null
↳ user_feedbackstring | null"positive", "negative", or null. Set via PATCH .../messages/{chat_id}/feedback.
↳ dataobjectassistant-only. data.knowledge.{relevant_sources,summaries,detailed_chunks} are genuinely the same fields the live /chat response returns (persisted verbatim by _persist_turn, G14) — not a relabeled stand-in. data.knowledge.knowledge_evidence is a SEPARATE, lossier shape kept for cross-turn memory only; don't treat it as a sources-panel substitute. rag_context is the one live field not persisted. See KnowledgeChatLogs' docstring.
↳ has_attachmentbooleanDefault: false
↳ attachment_urlstring | null

Other responses

  • 422 Validation Error
import os
import requests

API_ROOT = "https://futuresmart.ai/document-intelligence/api"
API_KEY = os.environ["DOCUMENT_INTELLIGENCE_API_KEY"]

r = requests.get(
    f"{API_ROOT}/v1/YOUR_COLLECTION_ID/sessions/YOUR_SESSION_ID",
    headers={"x-api-key": API_KEY},
    timeout=120,
)
r.raise_for_status()
print(r.json())

Response shape (200)

JSON
{
  "session_id": "<session_id>",
  "knowledge_id": "<knowledge_id>",
  "exchange_count": 0,
  "messages": [
    {
      "n": 0,
      "chat_id": "<chat_id>",
      "role": "<role>",
      "message": "<message>",
      "has_attachment": false
    }
  ]
}