Datalumo
Docs · Install
View markdown

Widget SDK

The browser SDK mounts Datalumo search and chat on your site. It loads presentation settings from your widget, streams answers, stores chat history in the browser, and reports analytics. For dashboard settings see Chat and Search. For custom UIs or server-side calls, use the Widget API instead. To connect a chatbot to your own systems, see Chat actions.

Script URL:

https://datalumo.app/widget/v1/datalumo.js

The SDK exposes a global Datalumo object. The API origin is taken from the script URL, so one build works on any host.

#Before you embed

  1. Create a search or chat widget in the dashboard.
  2. Add your site domain under the widget's websites list (for example example.com or www.example.com). Browser requests are authorised by the page origin.
  3. Copy the widget key. It looks like {organisation}/{widget} (two public ids). It is safe to put in HTML.

You can also copy a ready-made snippet from the widget page under Add to your site.

#Install

Paste before </body>:

#Chat bubble

<script src="https://datalumo.app/widget/v1/datalumo.js"></script>
<script>
  Datalumo.chat('ORG_ID/WIDGET_ID').mount();
</script>

#Search (modal trigger in a container)

<!-- min-height matches the launcher so content below does not jump while the SDK loads -->
<div id="search" style="min-height: 2.5rem"></div>

<script src="https://datalumo.app/widget/v1/datalumo.js"></script>
<script>
  Datalumo.search('ORG_ID/WIDGET_ID', { target: '#search' }).mount();
</script>

Load the script once per page. Call .mount() after the DOM nodes you pass as target or trigger exist.

Avoid layout shift: give the search target a fixed min-height of 2.5rem (the launcher height) in your HTML. The SDK also reserves that height on mount() when the target is still empty, which covers the config-fetch gap after the script runs. Pre-sizing in HTML is still best so nothing moves before JS runs.

#Entry points

Call Use when
Datalumo.chat(key, options?) Chatbot widget (bubble, sidebar, or inline)
Datalumo.search(key, options?) Search widget (modal or inline)
Datalumo.headless(key, options?) No UI — search, chat, events, and conversation APIs yourself
Datalumo.markdown(text) Turn markdown from chat/summarize into sanitised HTML (headless UIs)

Every call returns a handle. For chat and search, call .mount() to start. Headless is ready immediately.

#Shared options

These apply to chat(), search(), and headless():

Option Type Description
user { id, hash? } Tag the visitor. With a server-signed hash, conversation history and ownership work (see Identity).
context object or false Extra page context sent with chat. By default the SDK adds the current page URL and title. Pass an object to merge static fields over that, or false to send no context.
token string Preview Bearer token. Only for the in-app preview; do not use on your public site.

On mount, the SDK loads config from the server, then merges your overrides. Dashboard settings are the defaults; anything you pass in the second argument wins except branding (plan-controlled).

Option Type Description
target CSS selector Where to place the UI. Required for inline chat and for search when you want the SDK-rendered box. Optional mount point for bubble/sidebar.
trigger CSS selector Use your own element(s) instead of the default launcher or search box. See variants below.
variant string Override the shape from the dashboard. Chat: bubble | sidebar | inline. Search: modal | inline.
side string Chat sidebar only: right (default) or left.
width number Chat sidebar only: panel width in px (clamped 280–720, default 380).
content CSS selector Chat sidebar only: element that receives padding when open (default: document.documentElement).
show_images boolean Chat: show source images in answers as thumbnails (click to expand). Default off; set in the dashboard under Advanced.
show_result_thumbnails boolean Search: thumbnail on the right of each result. Chat: card under the answer for each cited page that has one. Default off; set on Look.
theme string auto | light | dark
accent_light hex string Accent colour in light mode (for example #18181b)
accent_dark hex string Accent colour in dark mode
placeholder string Input placeholder
greeting string Chat only: first assistant message
prompts string[] Chat only: suggested questions shown as chips while the thread is empty (max 6). Click sends the question. Override from the SDK or set in the dashboard.
avatar string Chat only: image URL for the assistant
summary boolean Search only: AI summary above results (uses answer credits)
speech_input boolean Microphone button where the browser supports it
debounce number Search only: ms to wait after typing before searching (default 1000)
default_language string Fallback UI language when the browser language is unsupported

#Suggested questions (chat)

On an empty conversation the chat can show chips under the greeting. Visitors click a chip to send that question immediately.

Set them in the dashboard (Suggested questions: type and press Enter, like domains), or pass prompts when mounting:

Datalumo.chat('ORG_ID/WIDGET_ID', {
  variant: 'inline',
  target: '#help',
  prompts: [
    'How does pricing work?',
    'What is a refund?',
    'How do I get started?',
  ],
}).mount();

Limits: up to 6 prompts, 100 characters each. Chips hide after the first message and return on New conversation.

#Chat variants

Bubble (default) — floating launcher opens a panel. No target needed.

Datalumo.chat('ORG_ID/WIDGET_ID').mount();

Custom launcher — hide the default bubble and open from your own control(s). Any matching element toggles the panel; you can also call open() / close() / toggle() on the handle:

<button id="open-help">Ask us</button>

<script src="https://datalumo.app/widget/v1/datalumo.js"></script>
<script>
  const chat = Datalumo.chat('ORG_ID/WIDGET_ID', {
    trigger: '#open-help', // or '.open-chat' for several buttons
  }).mount();

  // Optional: open from your own code without a click binding
  // chat.open();
</script>

The default floating bubble is not rendered when trigger is set. Use a CSS selector that exists when .mount() runs.

Sidebar — full-height panel docked to the side. When open, the SDK pads the page (<html> by default) so content is pushed aside rather than covered. Uses the same floating bubble launcher as the bubble variant (or your own trigger).

Datalumo.chat('ORG_ID/WIDGET_ID', {
  variant: 'sidebar',
  // side: 'left',      // default: 'right'
  // width: 400,        // px, default 380
  // content: 'main',   // pad this element instead of <html>
  // trigger: '#help',  // optional host launcher
}).mount();

No host markup is required. On viewports ≤ 640px the panel goes full-bleed and page padding is skipped. Prefer content when only a main column should shift (for example a flex layout with a fixed header).

Inline — panel fills a container (set a height on the container):

<div id="help" style="height: 560px;"></div>
<script>
  Datalumo.chat('ORG_ID/WIDGET_ID', {
    variant: 'inline',
    target: '#help',
  }).mount();
</script>

#Search variants

Modal (default) — Cmd/Ctrl+K opens a full search overlay. Optionally render a search-box trigger into target, or bind your own trigger.

// Keyboard only (Cmd/Ctrl+K)
Datalumo.search('ORG_ID/WIDGET_ID').mount();

// Trigger box inside #search
Datalumo.search('ORG_ID/WIDGET_ID', { target: '#search' }).mount();

// Your own button(s) open the modal
Datalumo.search('ORG_ID/WIDGET_ID', { trigger: '.open-search' }).mount();

Inline — input with results listed underneath.

// SDK renders the input into #search
Datalumo.search('ORG_ID/WIDGET_ID', {
  variant: 'inline',
  target: '#search',
}).mount();

// Use an existing <input>; results appear after it (or in target)
Datalumo.search('ORG_ID/WIDGET_ID', {
  variant: 'inline',
  trigger: '#my-search-input',
  target: '#results', // optional
}).mount();

#Widget methods

After mount(), chat and search handles support:

Method Applies to Description
mount() both Start loading config and render. Safe to call once; returns the same handle.
destroy() both Remove the widget from the page.
open() both Open the bubble panel or search modal.
prefill(text) chat Open the panel and put plain text in the composer. Does not send. Caps at 280 characters. First-party docs use this for ?ask=; customer embeds should not honour that query.
ask(text) chat Open the panel and send a message (search "Ask assistant" handoff).
close() both Close the panel or modal.
toggle() chat Open if closed, close if open.
on('action', handler) chat Listen when the visitor confirms a page action. handler receives the same detail as datalumo:action.
off('action', handler) chat Remove a page-action listener.

Imperative methods queue until the widget is ready, so you can call them right after mount().

#Headless client

For a fully custom UI:

const dl = Datalumo.headless('ORG_ID/WIDGET_ID', {
  user: { id: userId, hash }, // optional, signed
  context: { product: 'billing' }, // optional
});

// Search
const hits = await dl.search('refund policy', { limit: 8 });

// Chat (full reply)
const reply = await dl.chat('How do refunds work?');
// reply: { text, conversationId, answerId, sources, actions }

// Chat (stream tokens)
await dl.chat('How do refunds work?', {
  conversationId: reply.conversationId,
  onToken: (delta, full) => { /* update UI */ },
  onStatus: (status) => { /* searching | web_searching | sending | offering */ },
});

// Optional AI summary of search results
const summary = await dl.summarize('refund policy', {
  onToken: (delta, full) => {},
});
// summary: { summarized, text }

// Analytics (fire-and-forget)
dl.trackEvent('click', { url: 'https://…', rank: 0, source: 'result' });

// Render markdown safely
container.innerHTML = Datalumo.markdown(reply.text);

Useful properties and helpers on the client:

Member Description
sessionId Analytics session for this tab (auto-created and reused)
identify(user) Set or clear { id, hash } after login
fetchConfig() Presentation config JSON from the server
listConversations() History for a signed visitor
getConversation(id) One transcript
keepConversation(id, kept?) Exempt from expiry (kept: false releases)
deleteConversation(id) Delete one conversation
deleteConversations() Delete all for this visitor

When answer credits run out, chat returns 402 with plain search hits in data. The official chat widget renders those results; a headless client should do the same from the 402 body (no need to call search separately).

#Signed visitor identity

To restore chat history for a logged-in user, or to list/keep/delete conversations, prove the user id on your server with the widget's identity signing secret (dashboard → widget → For developers):

$hash = hash_hmac('sha256', (string) $userId, $signingSecret);

Pass the result into the SDK (never ship the signing secret to the browser):

// At init (preferred so every message is tagged)
const chat = Datalumo.chat('ORG_ID/WIDGET_ID', {
  user: { id: String(userId), hash },
}).mount();

// Or after login
const headless = Datalumo.headless('ORG_ID/WIDGET_ID');
headless.identify({ id: String(userId), hash });
await headless.listConversations();

A bad hash is rejected; Datalumo will not treat a failed proof as an anonymous visitor.

See Authentication and Conversations.

#Analytics beacon (server-rendered results)

If you search on your server and render your own result list, load the lightweight analytics script so clicks still join the same funnel. Reuse the session_id your server received from search.

<script
  src="https://datalumo.app/widget/v1/datalumo-analytics.js"
  data-widget="ORG_ID/WIDGET_ID"
  data-session="SESSION_FROM_SERVER"
></script>

<a href="https://example.com/docs/refunds"
   data-dl-click
   data-dl-rank="0"
   data-dl-source="result">
  Refunds
</a>
Attribute Required Description
data-widget yes Widget key {organisation}/{widget}
data-session no Session id from the search response; omit to mint a new tab session
data-base no API origin override (defaults to the script origin)
data-dl-click on links Marks a result link for auto click tracking
data-dl-url no Override URL (defaults to href)
data-dl-rank no Result position
data-dl-source no e.g. result, summary

Or initialise manually:

const analytics = DatalumoAnalytics.init({
  key: 'ORG_ID/WIDGET_ID',
  sessionId: '…',
});
analytics.trackClick({ url: 'https://…', rank: 0, source: 'result' });

Your domain must still be on the widget's website list.

#Presentation config

On mount the SDK requests:

GET /api/v1/{organisation}/widgets/{widget}/config

Typical payload:

{
  "type": "chatbot",
  "name": "Help",
  "variant": "bubble",
  "accent_light": "#18181b",
  "accent_dark": "#fafafa",
  "theme": "auto",
  "branding": true,
  "summary": false,
  "speech_input": false,
  "placeholder": "",
  "greeting": "",
  "prompts": ["How does pricing work?"],
  "avatar": null,
  "debounce": 1000,
  "default_language": "en"
}

Responses may be cached for up to five minutes. branding follows your plan; clients cannot turn it off by override.

Most appearance and behaviour options are edited in the dashboard (shape, colours, greeting, suggested questions, AI summary, voice input, default language). Use SDK overrides for per-page differences (target, trigger, prompts, one-off theme).

#Languages

UI chrome (placeholders, buttons, status text) follows the visitor's browser language when supported, otherwise the widget's default_language, otherwise English.

Locale Language
en English
nl Dutch, Nederlands
de German, Deutsch
fr French, Français
es Spanish, Español
it Italian, Italiano
pt-BR Portuguese (Brazil), Brazilian Portuguese, Português
zh-Hans Chinese (Simplified), Mandarin, China, Singapore
zh-Hant Chinese (Traditional), Taiwan
zh-HK Chinese (Hong Kong), Cantonese, Hong Kong, Macau
ja Japanese, 日本語
ko Korean, 한국어
th Thai, ไทย

Browser tags such as zh-CN and zh-SG use Simplified / Mandarin. zh-TW uses Traditional. zh-HK and zh-MO use the Hong Kong / Cantonese locale. pt and pt-PT use Brazilian Portuguese.

#FAQ

What languages does Datalumo support?

Search and chat are multilingual. They work in most languages, even when the question and the source pages are not the same language. An English query can still find the right passages in a Cantonese knowledge base. Chat replies in the language of the question.

The table above is only the widget chrome: placeholders, buttons, status text. Those follow the visitor's browser when we have that locale. You can pin a language in the widget settings if you do not want that. Anything else falls back to English.

#Troubleshooting

Symptom Check
Widget does not load / config fails Domain is on the widget's website list; key is {org}/{widget} public ids
Console: no element matches target / trigger Selector exists when .mount() runs
Chat says wrong widget type Use a chatbot widget with Datalumo.chat, not a search widget
Conversation history empty Pass signed user.id + user.hash; signing secret must match
No analytics in the dashboard Domain allowlist; wait for first search or chat; for custom results use the analytics beacon
Branding still shows Controlled by plan, not by client override