How do I enable search mode?

Set data-search-mode-enabled="true". The panel gets a Search tab beside the chat that lists matching pages without writing an answer. To open it from your site’s own search button, and carry over what the reader typed:
<script async src="https://widget.kelu.dev/kelu-widget.js"
  data-knowledge-base-id="YOUR_KNOWLEDGE_BASE_ID"
  data-client-key="kl_pk_..."
  data-search-mode-enabled="true"
  data-modal-override-open-selector-search=".my-search-button"
  data-open-query-from="#docs-search"></script>
See Search mode for every search setting.

Can I listen to events?

Yes. The widget reports every interaction, from the panel opening to an answer being rated:
window.Kelu("onAskAIQuerySubmit", ({ question }) => {
  analytics.track("Docs AI Question", { question });
});
See Events for the full list and payloads.

How can I attach the widget to a custom button?

Hide the launcher and name your own element:
<button id="ask-ai">Ask AI</button>

<script async src="https://widget.kelu.dev/kelu-widget.js"
  data-knowledge-base-id="YOUR_KNOWLEDGE_BASE_ID"
  data-client-key="kl_pk_..."
  data-launcher-button-hidden="true"
  data-modal-override-open-selector="#ask-ai"></script>
Use data-modal-override-open-selector-ask-ai to open the chat tab, or -search for the Search tab. To add a button to a docs header you cannot edit, see Your own button in the header.

What are the rate limits?

Each client key allows 600 requests a minute by default, shared by every reader using it. To set a different limit, fill in Rate limit when you create the key. Over the limit, the reader sees:
That's a lot of questions at once — give it a moment and try again.
Your plan’s monthly question allowance is a separate limit. When it runs out, readers see that the assistant has reached its question limit for the month.

The widget does not appear

  • The tag is incomplete. It needs data-client-key and either data-knowledge-base-id or data-widget-id. Without them the widget does nothing and logs nothing.
  • There is no launcher by design with data-launcher-button-hidden="true", data-render-on-load="false", or data-view-mode="search".
  • A Content Security Policy blocked it. See below.

How do I fix a configuration error?

When the panel shows an error instead of an answer, open your browser’s developer tools.
  • Console: a failed answer logs [Kelu] answer failed — code=… status=… with a request_id to quote to support.
  • Network: find the failed request to app.kelu.dev (or your data-base-url) and read its status.
StatusUsual cause
401The client key is missing, mistyped or revoked
403The page’s origin is not in the key’s Allowed origins, or not in the widget’s own; the widget is disabled; the key belongs to another knowledge base; the knowledge base is internal; or a reCAPTCHA token was missing. The response body says which
404data-widget-id names no widget, or a widget of another knowledge base than the key
429The key’s rate limit. See above
With data-widget-id, the widget first loads that widget’s settings, and every question it sends names the widget. If the id is wrong, the widget is disabled, or the page’s origin is not in the widget’s own Allowed origins, the launcher still appears but every question fails: 404 for a wrong id, 403 for the other two. The console says which, in a line such as [Kelu Widget] configuration refused for widget <id> — status=403 code=ORIGIN_NOT_ALLOWED. localhost always passes the widget’s own list, but not the key’s.

Why is the conversation still there after a page load?

By design. The conversation is kept for the browser tab and cleared when the tab closes. Set data-persist="none" to start fresh on every page load, or call KeluWidget.reset() to clear it. In a single-page app the widget also stays on the page across route changes. To show it on some routes only, see Widget lifecycle.

Can readers in mainland China use the widget?

Only if your deployment does not require reCAPTCHA. When it does, the widget footer says Protected by reCAPTCHA, every question needs a Google reCAPTCHA token, and Google is blocked in mainland China.
  • hCaptcha is not supported. data-bot-protection-mechanism="hcaptcha" logs a warning and changes nothing.
  • data-bot-protection-mechanism="none" stops the widget loading reCAPTCHA, but then every question is refused where a token is required.

How do I change the widget’s language?

Set data-language to one of these codes:
CodeLanguageCodeLanguage
enEnglish (default)jaJapanese
esSpanishkoKorean
frFrenchzhChinese
deGermanruRussian
ptPortuguesecsCzech
itItalianfiFinnish
nlDutch
data-language="fi"
  • This changes the widget’s own text: buttons, labels, placeholders and error messages.
  • A region is ignored (pt-BR is pt). An unknown code falls back to English.
  • Text you set yourself, such as data-launcher-button-text, is used in every language.
It does not change the answers. They are in English unless the reader asks for another language (“answer in Spanish”), or a customization assigned to the widget sets a language. A question written in another language still gets an English answer.

Why does the widget look too small or too large?

The widget sizes itself in pixels, so your site’s base font size does not affect it. To make everything larger or smaller, set data-scale-factor:
data-scale-factor="1.15"
It multiplies every text size and control size. To change only the text sizes, see Theming.

Why are parts of the widget invisible?

data-brand-color colors the launcher, buttons and links. A color close to your page or panel background, such as white, makes them disappear. Pick a color with more contrast, or set the part’s own color: data-anchor-color for links, or a component style such as data-launcher-button-background-color.

How do I fix CSP errors?

If your site sends a Content Security Policy, allow these:
DirectiveAddFor
script-srchttps://widget.kelu.devThe widget script
connect-srchttps://app.kelu.dev and wss://app.kelu.dev, or your data-base-url hostQuestions, answers, search and settings. Answers stream over the wss:// connection when it is allowed
style-src'unsafe-inline'The widget’s own styles
img-srcThe hosts of your logo and of images in your docsYour logo, and images shown in answers
script-src, frame-srchttps://www.google.com, https://www.gstatic.comreCAPTCHA, only where your deployment requires it
The browser console names each blocked request and the directive that blocked it.