How do I enable search mode?
Setdata-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:
Can I listen to events?
Yes. The widget reports every interaction, from the panel opening to an answer being rated:How can I attach the widget to a custom button?
Hide the launcher and name your own element: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:The widget does not appear
- The tag is incomplete. It needs
data-client-keyand eitherdata-knowledge-base-idordata-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", ordata-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 arequest_idto quote to support. - Network: find the failed request to
app.kelu.dev(or yourdata-base-url) and read its status.
| Status | Usual cause |
|---|---|
401 | The client key is missing, mistyped or revoked |
403 | The 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 |
404 | data-widget-id names no widget, or a widget of another knowledge base than the key |
429 | The key’s rate limit. See above |
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. Setdata-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?
Setdata-language to one of these codes:
| Code | Language | Code | Language |
|---|---|---|---|
en | English (default) | ja | Japanese |
es | Spanish | ko | Korean |
fr | French | zh | Chinese |
de | German | ru | Russian |
pt | Portuguese | cs | Czech |
it | Italian | fi | Finnish |
nl | Dutch |
- This changes the widget’s own text: buttons, labels, placeholders and error messages.
- A region is ignored (
pt-BRispt). An unknown code falls back to English. - Text you set yourself, such as
data-launcher-button-text, is used in every language.
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, setdata-scale-factor:
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:| Directive | Add | For |
|---|---|---|
script-src | https://widget.kelu.dev | The widget script |
connect-src | https://app.kelu.dev and wss://app.kelu.dev, or your data-base-url host | Questions, answers, search and settings. Answers stream over the wss:// connection when it is allowed |
style-src | 'unsafe-inline' | The widget’s own styles |
img-src | The hosts of your logo and of images in your docs | Your logo, and images shown in answers |
script-src, frame-src | https://www.google.com, https://www.gstatic.com | reCAPTCHA, only where your deployment requires it |