Skip to main content

Chat API

Stream conversational answers grounded in your indexed documents — designed for embedding in widgets, websites, and documentation portals.

info

The public chat API uses Server-Sent Events streaming exclusively. There is no non-streaming POST /v1/chat endpoint on the public API key surface; the dashboard's non-streaming chat lives behind JWT auth at /api/v1/teams/:teamID/chat and is not part of this reference.

Streaming chat

Endpoint

POST /v1/chat/stream

Authenticated with the X-ZenSearch-Key header. CORS is wide-open so this endpoint can be called from a widget embedded on any third-party site (auth is header-based, not cookie-based, so this is safe).

Request body

{
"message": "What is our refund policy?",
"conversation_id": "00000000-0000-0000-0000-000000000abc",
"collection": "00000000-0000-0000-0000-000000000def"
}

Parameters

ParameterTypeRequiredDescription
messagestringYesUser message. Max 4000 characters.
conversation_idUUIDNoContinue an existing conversation. Omit to start a new one.
collectionUUIDNoPin the search to a single collection. Must be in the API key's allowed collections list. Omit to use the key's default collection.

The model and system prompt are not selectable per request — they are configured on the API key in the dashboard (widget_model_id, widget_context). This keeps the surface predictable for embedded widgets and prevents prompt-injection of system context from the visitor side.

Response stream

Responses use Server-Sent Events. The agent and chat-platform layers emit the events documented in the features overview. The chat-platform layer additionally emits content, sources, follow_up, and done events that visitor-facing widgets typically consume:

event: content
data: {"delta": "Our refund policy "}

event: content
data: {"delta": "allows returns within 30 days..."}

event: sources
data: {"sources": [{"id": "...", "title": "Refund Policy", "url": "https://..."}]}

event: follow_up
data: {"questions": ["What about international orders?", "How do I start a return?"]}

event: done
data: {"usage": {"prompt_tokens": 1500, "completion_tokens": 200, "total_tokens": 1700}}

Errors emitted on the stream are sanitized to a generic visitor-safe message; the original error is logged server-side.

JavaScript example

const res = await fetch('https://your-domain.com/v1/chat/stream', {
method: 'POST',
headers: {
'X-ZenSearch-Key': 'zsk_pk_xxx...',
'Content-Type': 'application/json',
},
body: JSON.stringify({ message: 'What is our refund policy?' }),
});

const reader = res.body!.getReader();
const decoder = new TextDecoder();
while (true) {
const { value, done } = await reader.read();
if (done) break;
process.stdout.write(decoder.decode(value));
}

Conversation history

Conversation listing, retrieval, and deletion are dashboard-only operations under /api/v1/teams/:teamID/conversations. They are not exposed on the public /v1/... API key surface.

To continue a conversation, store the conversation_id returned in your widget session and pass it back on the next request.

Error responses

CodeDescription
400Invalid request body
401API key missing or invalid
402Billing query limit exceeded
403Collection not allowed for this key
429Rate limit exceeded

Next steps