The built-in API cards
Messaging API — ask over HTTP
The Messaging API lets your own application ask the assistant over HTTP and get grounded, cited answers — the programmatic sibling of the website widget. Every answer is access-controlled and audited exactly like the web chat. This article is the complete reference.
Two kinds of key
- The channel's own key (shown on the card) answers as a guest, capped at the guest access groups you choose — with none chosen it stays silent. Use it for app-wide or anonymous access.
- A person's own key (Users → Edit → Chat channels → Generate key) answers with that user's own document access, exactly as if they asked in the web chat. Use it so each person sees only what they're allowed to.
Setup
- Chat channels → Messaging API/MCP: name it, and pick the guest access groups the shared key may answer from.
- For per-person access, open Users → Edit → Chat channels and Generate a key for each person; copy it from there.
- Send the key in an Authorization: Bearer header. A messaging key can never ingest.
Endpoints
- POST /api/v1/messages — ask a question; a cited answer or an abstention
- POST /api/v1/messages/stream — the same, streamed as Server-Sent Events (delta / reset / final events)
- POST /api/v1/recall — ask in one call, choosing where to look; the answer with up to 20 sources (see Recall)
- GET /api/v1/conversations — list the caller's conversations
- GET /api/v1/conversations/{id} — a conversation's messages and citations
- DELETE /api/v1/conversations/{id} — erase a conversation
Message fields
| text | The question, in any language. |
|---|---|
| end_user | Optional caller id (1–128 chars: letters, digits, . _ : -, starting alphanumeric). With the shared channel key it keeps each end user's conversation memory separate; with a personal key it is ignored — the key already is the person. |
| conversation_id | Pass the id from a previous answer to continue that conversation. |
The answer
Responses carry the answer text, abstained (true when the honest answer was “not in the knowledge base”), conversation_id, and citations — the numbered sources behind the text, each with its passage id, breadcrumb and source location. Personal-key conversations are the same ones the person sees in the web chat.
Example
A single question, in curl:
curl -X POST <base>/api/v1/messages \
-H "Authorization: Bearer <key>" \
-H "Content-Type: application/json" \
-d '{"text":"What is the refund window?",
"end_user":"u-4471"}'
The same call in Python, with error handling:
import requests
resp = requests.post(
"<base>/api/v1/messages",
headers={"Authorization": "Bearer <key>"},
json={"text": "What is the refund window?", "end_user": "u-4471"},
)
if resp.status_code == 429:
print("rate limited — slow down")
elif not resp.ok:
# error bodies are {"detail": "..."}
print(resp.status_code, resp.json().get("detail"))
else:
data = resp.json()
if data["abstained"]:
print("not in the knowledge base")
else:
print(data["text"])
for c in data["citations"]:
print(f"[{c['n']}] {c['breadcrumb']}")
And in JavaScript, using fetch on Node 18 or later:
const resp = await fetch("<base>/api/v1/messages", {
method: "POST",
headers: {
"Authorization": "Bearer <key>",
"Content-Type": "application/json",
},
body: JSON.stringify({ text: "What is the refund window?", end_user: "u-4471" }),
});
const data = await resp.json();
if (resp.status === 429) {
console.error("rate limited — slow down");
} else if (!resp.ok) {
// error bodies are { detail: "..." }
console.error(resp.status, data.detail);
} else if (data.abstained) {
console.log("not in the knowledge base");
} else {
console.log(data.text);
for (const c of data.citations) console.log(`[${c.n}] ${c.breadcrumb}`);
}
Behaviour worth knowing
- Rate limit: 60 messages per minute per channel and end user (429 when exceeded).
- A guest key with no guest groups configured answers with the abstention — deliberately.
- If answering itself fails, the API returns 503 with an apology in text and error: true, rather than a bare error — check error (or the HTTP status) alongside abstained.
- Every question is audited with its consulted and cited passages, like every other surface.
Machine-readable spec for Postman/codegen: /api/v1/openapi.json.
Last updated 20 Sep 2026