Skip to main content
Workspace admin

Query endpoints

Two endpoints, one request shape. Both need a scoped widget token (see Developer access and widget tokens), and both are stateless: each call is answered on its own, with no memory of the last one.

Ask a question​

curl -X POST "$BASE_URL/kb-api/api/v1/kb/$KB_ID/widget/query" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"question": "What is the notice period for a fixed-term contract?"}'

Request​

FieldTypeNotes
questionstringRequired. The question, in plain language. The field is question, not query.
top_kintegerAccepted and ignored.
optionsobjectAccepted and ignored.
conversation_idstringAccepted and ignored. These endpoints hold no conversation.

Response​

{
"answer": "A fixed-term contract requires 30 days' written notice.",
"citations": [
{ "index": 1, "doc_id": "doc_01J...", "kb_id": "kb_01J...", "source_span": { } }
],
"provenance": "generated",
"abstained": false
}
FieldTypeNotes
answerstringThe grounded answer. When abstained is true, this is the refusal message.
citationsarrayWhat the answer was drawn from. Empty when nothing was cited.
citations[].indexintegerThe marker used in answer.
citations[].doc_idstringThe document the passage came from.
citations[].kb_idstringThe knowledge base it belongs to.
citations[].source_spanobjectAn opaque pointer to the passage. Pass it back to us when you report a problem; don't parse it.
provenancestringgenerated, abstained or qa_shortcut (answered from a curated question and answer pair).
abstainedbooleanTrue when Elie declined to answer. Always present.

Fields with no value are left out rather than sent as null.

Declining to answer is a success, not an error. It arrives as 200 with abstained: true, and it means the knowledge base held no evidence good enough to answer from. Treat it as an answer your interface should show, not as a failure to retry.

Stream the answer​

curl -N -X POST "$BASE_URL/kb-api/api/v1/kb/$KB_ID/widget/query/stream" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"question": "What is the notice period?"}'

Same request body. The response is text/event-stream, and every frame carries a JSON object whose type repeats the event name.

EventPayloadWhat to do with it
queued{ "type": "queued", "stage": "..." }The answer is waiting its turn. Show that you are working on it.
heartbeat{ "type": "heartbeat", "stage": "..." }Sent every 10 seconds or so to keep the connection open. Nothing to show.
token{ "type": "token", "text": "..." }Append text to what you are showing.
citations{ "type": "citations", "citations": [ ... ] }Show the sources. Same fields as above, plus text when the passage can be quoted. May arrive before the answer finishes.
abstain{ "type": "abstain", "answer": "..." }Replace everything shown so far with answer.
reset{ "type": "reset" }Discard everything shown so far and keep listening.
suggestions{ "type": "suggestions", "questions": [ ... ] }Optional follow-up questions to offer.
done{ "type": "done", "abstained": false }The answer is complete. Close the stream.
error{ "type": "error", "code": "...", "message": "..." }Writing the answer failed part-way. Show what you have, plus a failure notice.

Handle reset and abstain, or you will show text Elie has withdrawn: both exist because the service may retract a partial answer it no longer stands behind.

A refused request never becomes a stream. The token, the knowledge base, billing and the question are all checked before the response opens, so a refusal is an ordinary status code with a JSON body. That includes 503 dependency_unavailable with Retry-After when the service already has too many answers streaming.

Next​

Last updated on Something wrong with this page?