Widget API overview
/api/v1/{organisation}/widgets/{widget}/…
{widget} is the widget's public id. The key you paste into the embed snippet looks like {organisation}/{widget}. That public key is safe to put in the browser; it is not a secret.
#Endpoints
| Method | Path | Purpose | Uses answers |
|---|---|---|---|
GET |
/config |
Appearance and behaviour settings | No |
POST |
/search |
Search your content | No |
POST |
/chat |
AI answer (optional streaming) | Yes |
POST |
/summarize |
Short AI summary of top results | Yes |
POST |
/events |
Feedback and click analytics | No |
* |
/conversations… |
History for signed-in visitors | No |
See Authentication for domain, secret key, and API key rules.
#Config
GET /widgets/{widget}/config
Responses may be cached for up to five minutes.
{
"type": "chatbot",
"name": "Help",
"variant": "bubble",
"accent_light": "#18181b",
"accent_dark": "#fafafa",
"theme": "auto",
"branding": true,
"summary": false,
"speech_input": false,
"placeholder": "",
"greeting": "",
"prompts": [],
"avatar": null,
"debounce": 1000,
"default_language": "en"
}
prompts is chat-only: short suggested questions (max 6) rendered as chips on an empty thread.
branding reflects your plan. On plans that show the Datalumo badge, clients cannot turn it off by ignoring this field.
#Session ids
Search and chat accept an optional session_id. If you omit it, Datalumo creates one and returns it. Reuse the same id from search through clicks and chat so analytics stay connected for that visit.
#Search
POST /api/v1/{organisation}/widgets/{widget}/search
Auth: widget domain, widget secret, or organisation API key with search
Widget type: search or chatbot
Search across the sources linked to the widget. Search does not use answer credits.
#Body
| Field | Type | Required | Description |
|---|---|---|---|
query |
string | yes | Max 1024 characters |
filters |
object | no | Optional filters |
sort |
string | no | Optional sort |
limit |
int | no | 1-50, default 10 |
session_id |
string | no | Analytics session; created for you if omitted |
#Response
{
"data": [
{
"id": "0194b…",
"external_id": "wp-42",
"name": "Hello world",
"url": "https://example.com/hello-world#section",
"snippet": "…matching text…",
"score": 0.87,
"meta": null,
"thumbnail": "https://example.com/hello-world.jpg"
}
],
"session_id": "…"
}
url is the link you can show as a citation (page URL, with a section anchor when available).
#Example
curl -sS -X POST \
-H "Authorization: Bearer $WIDGET_SECRET" \
-H "Content-Type: application/json" \
-d '{"query": "how does pricing work", "limit": 5}' \
"https://datalumo.app/api/v1/{organisation}/widgets/{widget}/search"
#Chat
POST /api/v1/{organisation}/widgets/{widget}/chat
Auth: widget domain, widget secret, or organisation API key with chat
Widget type: chatbot
Billing: uses one answer from your plan. When you have no answers left, the API returns 402 with plain search hits in data (same shape as the search endpoint). No chat model is called. The official embed renders those results in the bubble.
#Body
| Field | Type | Required | Description |
|---|---|---|---|
query |
string | yes | Visitor message, max 2048 characters |
conversation_id |
string | no | Continue an existing conversation |
session_id |
string | no | Analytics session |
browser_language |
string | no | Language hint from the visitor's browser |
context |
object | no | Up to 10 string keys with simple values (for example page URL or title) |
user |
object | no | { "id": "…", "hash": "…" } signed visitor identity |
stream |
bool | no | Set true for a streamed response instead of one JSON payload |
#JSON response (stream false or omitted)
{
"data": {
"text": "…answer in markdown…",
"conversation_id": "…",
"answer_id": "…",
"sources": [
{
"id": "0194b…",
"external_id": "wp-42",
"name": "Hello world",
"url": "https://example.com/hello-world",
"snippet": "…",
"score": 0.91,
"meta": null
}
],
"actions": []
},
"session_id": "…"
}
#Streaming ("stream": true)
The response is a stream of server-sent events (text/event-stream):
| Event | Data | When |
|---|---|---|
status |
{ "step": "searching", "query": "…" } |
Looking up your knowledge sources |
status |
{ "step": "web_searching" } |
Searching allowlisted websites (chat action) |
status |
{ "step": "sending" } |
Calling a configured webhook (chat action) |
status |
{ "step": "offering" } |
Proposing a page action the visitor will confirm |
token |
{ "text": "…" } |
Next piece of the answer |
done |
{ "conversation_id", "session_id", "answer_id", "sources": […], "actions": […] } |
Finished |
error |
{ "message": "…" } |
Something failed mid-stream |
#Identity and conversation ownership
- With a valid
user.idanduser.hash, the conversation belongs to that verified visitor. - Continuing a verified conversation without the same proof returns 403.
- A bad hash returns 422.
See Conversations for listing history, reading transcripts, keeping conversations, and deletion.
Chatbots can also call optional chat actions you configure in the dashboard (allowlisted web search, signed webhooks, and page actions).
#When to use the embed instead
The official embed already handles streaming, conversation storage, visitor identity, and falling back to search when answers run out. Prefer it in the browser: see Install for mount options, headless mode, and the analytics beacon, or the chat and search product guides. Call these endpoints directly when you build a custom client or need server-side access.
Optional chatbot actions (allowlisted web search, signed webhooks, and page actions) are configured in the dashboard, not on this config endpoint.