Widget chat API
These are the endpoints the embedded widget itself uses. Document them if you are building your own chat interface rather than using the supplied widget.
All of them require widget key authentication:
X-Widget-Key: your_widget_key
Each path contains a chatbot id, but the chatbot actually used is the one the key belongs to. Changing the id in the path does not reach a different chatbot.
Get widget configuration
GET /chat/:chatbotPublicId/config
Everything a chat interface needs to render itself.
{
"success": true,
"data": {
"config": {
"name": "Northgate Assistant",
"primaryColor": "#6d5efc",
"theme": {},
"avatarUrl": "/uploads/1737465600000-a1b2c3.png",
"humanHandoffEnabled": true,
"welcomeMessage": "Hi! How can I help you today?",
"quickPrompts": ["What are your opening hours?"],
"allowLeadCapture": true,
"leadCaptureFields": { "name": true, "email": true, "phone": false }
}
}
}
Only public presentation fields are returned. The system prompt, model choice, indexed content and owner details are never exposed here.
avatarUrl is root-relative — resolve it against the API origin, not against
/api.
Send a message
POST /chat/:chatbotPublicId/message
{
"sessionId": "a-stable-per-visitor-id",
"message": "What are your opening hours?",
"conversationHistory": [],
"leadData": { "email": "visitor@example.com" }
}
| Field | Required | Notes |
|---|---|---|
sessionId | Yes | Identifies the conversation. Generate once per visitor and reuse it. |
message | Yes | The visitor's question |
conversationHistory | No | Prior turns, for context |
leadData | No | Attaches contact details to this conversation |
The response is a stream
This endpoint returns Server-Sent Events, not JSON.
Content-Type: text/event-stream
Three event types arrive:
token — one fragment of the answer. Many of these, in order.
event: token
data: {"token":"We're "}
done — the answer is complete.
event: done
data: {"sources":[],"sessionId":"...","messageId":"..."}
Keep messageId — it is what you pass to the
feedback endpoint.
error — generation failed.
event: error
data: {"message":"Something went wrong. Please try again."}
Concatenate the token values in arrival order to build the answer. Render it as
Markdown — the default system prompt asks for bold, lists and structure.
Consuming the stream
const res = await fetch(`${API_URL}/chat/${chatbotId}/message`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Widget-Key': apiKey,
},
body: JSON.stringify({ sessionId, message }),
});
const reader = res.body.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) break;
// Parse the SSE frames out of decoder.decode(value, { stream: true })
}
Follow-up suggestions
Answers end with a [SUGGESTIONS] line carrying three follow-up questions
separated by |:
[SUGGESTIONS] Do you deliver? | What's your returns policy? | Where are you based?
Strip that line out of the visible answer and render the three questions as tappable buttons. Leaving it in the message body shows the marker to your users.
Takeover
If an agent has taken over the session, the
chatbot does not generate a reply. The response indicates takenOver: true and
the human's messages arrive over the realtime channel instead.
Errors
| Status | Message | Meaning |
|---|---|---|
404 | Chatbot not found | The key's chatbot no longer exists |
429 | Message limit reached | The account's monthly message allowance is exhausted |
429 | Rate limited | Too many messages per minute — see Rate limits |
Get conversation history
GET /chat/:chatbotPublicId/history
Returns data.messages for a session — used to restore a conversation when a
visitor returns.
Submit feedback
POST /chat/:chatbotPublicId/feedback
Records a thumbs up or thumbs down against one reply.
{
"sessionId": "...",
"messageId": "...",
"feedback": "positive"
}
feedback is positive or negative. messageId comes from the done event.
Returns the resulting state:
{
"success": true,
"data": { "feedback": "positive" }
}
Rating a message positive when it is already positive removes the rating and
returns "feedback": null. That is what makes the thumbs buttons behave as
toggles — reflect the returned value in your UI rather than assuming the rating
was applied.
Errors
| Status | Message |
|---|---|
404 | Conversation not found |
404 | Message not found |
400 | Only assistant messages can be rated |
Ratings feed the satisfaction figures on the chatbot.