How to Use the Chat API
The Chat API lets you send messages to your bot and receive streaming responses programmatically. This is the same API that powers the embedded widget — you can use it to build custom integrations, mobile apps, or backend services.
Prerequisites
Section titled “Prerequisites”- Your bot must be set to Public privacy mode.
- You need your bot ID (find it in the Embed tab of the Playground).
Send a message (curl)
Section titled “Send a message (curl)”curl -N -X POST https://app.chatbotiq.eu/v1/public/chat/stream \ -H "Content-Type: application/json" \ -d '{ "bot_id": "YOUR-BOT-ID", "message": "How do I get started?", "session_id": "my-session-123" }'The -N flag disables curl’s output buffering so you see the streaming response in real time.
Understand the response stream
Section titled “Understand the response stream”The response is a Server-Sent Events (SSE) stream. Each line is prefixed with data: followed by a JSON object. Every event has a type field that tells you how to handle it:
type | Payload | What to do |
|---|---|---|
status | {"type":"status","stage":"retrieving"} then "generating" | Progress hints. Safe to ignore, or show a “thinking…” indicator. |
content | {"type":"content","content":"some text"} | A chunk of the answer. Concatenate the content fields to build the full response. |
replace | {"type":"replace","content":"full text"} | The answer was post-filtered. Discard what you’ve shown so far and replace it with this content. |
notice | {"type":"notice","code":"rag_unavailable",...} | A non-fatal warning (e.g. knowledge retrieval temporarily degraded). Optional to surface. |
done | {"type":"done","conversation_id":"...","assistant_message_id":"...","citations":[...]} | End of stream. Save conversation_id to continue the conversation. citations is present only if the bot has source citations enabled. |
error | {"type":"error","error":"content_filtered","message":"..."} | Generation failed or the response was blocked. Show message. |
The answer text arrives in the content field of content events — there is no token field, and done is an event type, not a boolean flag. conversation_id and citations appear only on the final done event.
A citation object looks like:
{ "page_title": "Getting Started", "source_url": "https://docs.example.com/start", "source_id": "…", "score": 0.92, "from_web_search": false}Send a message (JavaScript)
Section titled “Send a message (JavaScript)”async function chat(botId, message, sessionId, conversationId) { const response = await fetch('https://app.chatbotiq.eu/v1/public/chat/stream', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ bot_id: botId, message: message, session_id: sessionId, conversation_id: conversationId }) });
const reader = response.body.getReader(); const decoder = new TextDecoder(); let buffer = ''; let fullResponse = '';
while (true) { const { done, value } = await reader.read(); if (done) break;
buffer += decoder.decode(value, { stream: true }); const lines = buffer.split('\n'); buffer = lines.pop(); // keep any incomplete trailing line for the next read
for (const line of lines) { if (!line.startsWith('data: ')) continue; const event = JSON.parse(line.slice(6));
switch (event.type) { case 'content': // a chunk of the answer fullResponse += event.content; process.stdout.write(event.content); break; case 'replace': // post-filtered answer — replaces everything shown so far fullResponse = event.content; break; case 'done': // end of stream console.log('\nConversation ID:', event.conversation_id); if (event.citations) console.log('Citations:', event.citations); break; case 'error': console.error('\nError:', event.message); break; // 'status' and 'notice' events can be ignored } } }
return fullResponse;}Send a message (Python)
Section titled “Send a message (Python)”import requestsimport json
def chat(bot_id, message, session_id, conversation_id=None): response = requests.post( 'https://app.chatbotiq.eu/v1/public/chat/stream', json={ 'bot_id': bot_id, 'message': message, 'session_id': session_id, 'conversation_id': conversation_id, }, stream=True )
full_response = '' for line in response.iter_lines(): if not line or not line.startswith(b'data: '): continue event = json.loads(line[6:]) etype = event.get('type')
if etype == 'content': # a chunk of the answer full_response += event['content'] print(event['content'], end='', flush=True) elif etype == 'replace': # post-filtered answer — replaces what came before full_response = event['content'] elif etype == 'done': # end of stream print(f"\nConversation ID: {event['conversation_id']}") if event.get('citations'): print(f"\nCitations: {event['citations']}") break elif etype == 'error': print(f"\nError: {event.get('message')}") break # 'status' and 'notice' events can be ignored
return full_responseContinue a conversation
Section titled “Continue a conversation”To send follow-up messages, include the conversation_id from the first response:
curl -N -X POST https://app.chatbotiq.eu/v1/public/chat/stream \ -H "Content-Type: application/json" \ -d '{ "bot_id": "YOUR-BOT-ID", "message": "Tell me more about that", "session_id": "my-session-123", "conversation_id": "CONVERSATION-ID-FROM-FIRST-RESPONSE" }'Handle errors
Section titled “Handle errors”There are two kinds of failures: HTTP errors returned before the stream starts, and error events delivered inside the stream once it has begun (the HTTP status is already 200 at that point).
HTTP errors (before streaming)
Section titled “HTTP errors (before streaming)”| Status | Meaning | What to do |
|---|---|---|
| 400 | Missing/invalid field, or a required reCAPTCHA token was not provided | Check the request body. |
| 403 | Bot is not public, bot is inactive, request origin not in the bot’s allowed domains, free trial expired, or reCAPTCHA failed | Check the bot’s privacy mode, active status, and allowed domains. |
| 404 | Bot not found | Check the bot_id. |
| 429 | Rate limit exceeded | Wait and retry. Check the bot’s rate-limit settings. |
reCAPTCHA: if the deployment is configured with reCAPTCHA,
recaptcha_tokenis required — omitting it returns400and an invalid token returns403.
In-stream errors (after streaming starts)
Section titled “In-stream errors (after streaming starts)”Once the stream is open, failures arrive as an SSE error event rather than an HTTP status:
data: {"type":"error","error":"content_filtered","message":"..."}data: {"type":"error","error":"service_unavailable","message":"Service temporarily unavailable"}service_unavailable covers transient backend issues, including the workspace running out of credits (there is no 402 status on this endpoint). content_filtered means the response was blocked by the safety/grounding filters.
Related
Section titled “Related”- Chat API Reference — complete endpoint documentation
- Bot Settings Reference — rate limits and privacy settings