Skip to content

Chat API Reference

The public chat API lets you have streaming conversations with a ChatbotIQ bot programmatically. This is the same endpoint that powers the embedded widget.

For a practical guide with code examples, see Use the Chat API.


https://app.chatbotiq.eu/v1/public

Send a message and receive a streaming response via Server-Sent Events (SSE).

POST /v1/public/chat/stream

None required. The bot must be set to Public privacy mode.

FieldTypeRequiredDescription
bot_idUUIDYesThe bot’s unique identifier. Found in the Embed tab of the Playground.
messageStringYesThe user’s message / question.
session_idStringYesAn anonymous session identifier. Use any unique string (e.g., UUID) to track the conversation session.
conversation_idStringNoThe conversation ID returned from a previous response. Include this to continue an existing conversation.
recaptcha_tokenStringNoGoogle reCAPTCHA token for bot protection (if configured).
image_dataStringNoBase64-encoded image data (without the data: prefix). For multimodal models only.
image_media_typeStringNoMIME type of the image: image/jpeg, image/png, or image/webp. Required if image_data is provided.
Terminal window
curl -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": "unique-session-id"
}'

The response is a Server-Sent Events (SSE) stream. Each event is a line prefixed with data: followed by a JSON object. Every event carries a type discriminator.

typeShapeDescription
status{"type":"status","stage":"retrieving"}Progress hints (retrieving, then generating). Informational.
content{"type":"content","content":"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 prior content and replace it with this value.
notice{"type":"notice","code":"rag_unavailable","level":"warning","message_key":"..."}A non-fatal warning (e.g. retrieval degraded).
done{"type":"done","conversation_id":"uuid","assistant_message_id":"uuid","citations":[...]}End of stream. citations is present only when the bot has source citations enabled.
error{"type":"error","error":"code","message":"..."}Generation failed or was blocked. Common codes: content_filtered, service_unavailable, internal_error.

There is no token field and no boolean done field. The answer text is always in the content field of content (and replace) events, and conversation_id/citations appear only on the final done event.

{
"page_title": "Getting Started",
"source_url": "https://docs.example.com/start",
"source_id": "",
"score": 0.92,
"from_web_search": false
}

When the bot has links disabled (include_links_in_responses = false), source_url is returned as an empty string.

data: {"type":"status","stage":"retrieving"}
data: {"type":"status","stage":"generating"}
data: {"type":"content","content":"To"}
data: {"type":"content","content":" get started,"}
data: {"type":"content","content":" you need to..."}
data: {"type":"done","conversation_id":"abc-123","assistant_message_id":"def-456","citations":[{"page_title":"Getting Started","source_url":"https://docs.example.com/start","source_id":"…","score":0.92,"from_web_search":false}]}

Failures before the stream opens are HTTP status codes; failures after the stream has started arrive as an in-stream error event (the HTTP status is already 200).

StatusReason
400Missing/invalid field, or a required reCAPTCHA token was not supplied.
403Bot is not public, bot is inactive, origin not in the bot’s allowed domains, free trial expired, or reCAPTCHA verification failed.
404Bot not found.
429Rate limit exceeded (per-IP, and any per-bot limits).

Insufficient workspace credits do not produce a 402; they surface mid-stream as {"type":"error","error":"service_unavailable"}.

Public chat is rate-limited per client IP, per bot:

  • Baseline (per IP): 15 messages per minute, 120 per hour.
  • A workspace-level chat limit also applies (tied to the plan).
  • Additional per-bot limits can be set in Bot Settings; they are off by default and, when set, the more restrictive limit wins.

Retrieve the configuration needed to render the widget for a specific bot.

GET /v1/public/bots/{bot_id}/config

None required.

FieldTypeDescription
bot_idUUIDThe bot’s identifier.
bot_nameStringThe bot’s display name.
widget_settingsObjectAll widget customization settings (colors, placement, etc.).
suggested_questionsArray of stringsPre-configured suggested questions.
show_suggested_questionsBooleanWhether to display suggested questions.
suggested_questions_countIntegerHow many suggestions to show.
welcome_messageString or nullInitial greeting message.
warning_messageString or nullCustom rate-limit warning.
recaptcha_site_keyString or nullreCAPTCHA site key, if reCAPTCHA is enabled.
include_linksBooleanWhether links are included in responses.
languageStringBot’s response language code.
mcp_urlString or nullMCP integration endpoint, if configured.
handoff_enabledBooleanWhether human handoff is available.
handoff_triggerStringHandoff trigger mode: “button”, “auto”, or “both”.
handoff_offline_messageString or nullMessage shown when no agents are online.
handoff_agent_labelString or nullDisplay label for the human agent.
handoff_availableBooleanWhether agents are currently online.
allow_user_imagesBooleanWhether users can upload images.
contact_form_enabledBooleanWhether the contact form is enabled.
contact_form_triggerStringContact-form trigger mode.
contact_form_fieldsArray or nullContact-form field definitions.
contact_form_success_messageString or nullMessage shown after a successful submission.
contact_form_button_displayStringWhen to show the contact-form button (e.g. after_response).

Retrieve the message history for an existing conversation.

GET /v1/public/conversations/{conversation_id}/messages?session_id={session_id}
FieldRequiredDescription
session_idYesMust match the session that created the conversation (ownership check).
afterNoReturn only messages created after this message ID. Used for polling.

None required. The session_id must match the one used to create the conversation. This endpoint is itself rate-limited to 30 requests/minute per IP.

{
"messages": [
{
"id": "uuid",
"role": "user",
"content": "How do I get started?",
"citations": null,
"feedback": null,
"image_preview": null,
"created_at": "2026-03-30T12:00:00Z"
},
{
"id": "uuid",
"role": "assistant",
"content": "To get started, you need to...",
"citations": [
{
"page_title": "Getting Started",
"source_url": "https://docs.example.com/start",
"source_id": "",
"score": 0.92,
"from_web_search": false
}
],
"feedback": "positive",
"image_preview": null,
"created_at": "2026-03-30T12:00:01Z"
}
],
"conversation_status": "open"
}