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​