Skip to main content

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
The chatbot is resolved from the key, not the URL

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" }
}
FieldRequiredNotes
sessionIdYesIdentifies the conversation. Generate once per visitor and reuse it.
messageYesThe visitor's question
conversationHistoryNoPrior turns, for context
leadDataNoAttaches 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

StatusMessageMeaning
404Chatbot not foundThe key's chatbot no longer exists
429Message limit reachedThe account's monthly message allowance is exhausted
429Rate limitedToo 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" }
}
Sending the same rating twice clears it

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

StatusMessage
404Conversation not found
404Message not found
400Only assistant messages can be rated

Ratings feed the satisfaction figures on the chatbot.