Every deflector setting, with its dashboard field, script-tag attribute, JavaScript option and default. A dashboard setting applies to every page that uses the deflector. An attribute on the script tag, or an option passed to attach(), wins on that one page.
Leave an attribute off to keep the dashboard value. An empty value ("") is not the same: it replaces it. For on/off attributes, any value except "false" or "0" means on.
Setup
The Setup tab. Dashboard only.
| Setting | Default | What it does |
|---|
| Name | | How the deflector is listed |
| Knowledge base | | Where answers come from. To change it, pick a public key from the new knowledge base in the same save, then update the snippet on your page |
| Public client key | | The kl_pk_… key in the snippet. Must belong to the knowledge base |
| Allowed domains | Any site | Sites that may use the deflector, one per line. An exact origin (https://support.example.com), https://*.example.com for one level of subdomains, or a regular expression starting with ^. localhost always passes this list. The public key’s own allowed origins still apply, with no exception for localhost |
| Confidence threshold | 55% | Answers below it are not shown, and the ticket is filed |
| Voice | Knowledge base’s own persona | A customization to answer in |
| Live / Disabled | Live | A disabled deflector stops answering. The form submits as normal |
Script tag
| Attribute | Option | What it does |
|---|
data-knowledge-base-id | knowledgeBaseId | Required. The knowledge base to answer from |
data-client-key | clientKey | Required. The public client key (kl_pk_…) |
data-deflector-id | deflectorId | The deflector to load settings from. Without it, put the form and question field on the tag |
The Form wiring tab. Most forms need only the first two; the rest are under Advanced.
| Setting | Attribute | Option | Default | What it does |
|---|
| Form selector | data-deflect-form | deflectForm | | The <form> to intercept |
| Question field | data-deflect-question-field | deflectQuestionField | | The field where the reader describes the problem |
| Submit element | data-submit-element-query-selector | submitSelector | The form | The element whose event starts the answer. Set it when the button is outside the form |
| Intercept on | data-submit-element-event-type | submitEvent | onSubmit | onSubmit (Submit event) or onClick (Click event) |
| Submit button | data-submit-button-query-selector | submitButtonSelector | The submit element | Disabled while the answer is generated |
| Anchor element | data-anchor-element-query-selector | anchorSelector | The submit button, then the form | Where the notice, progress line and reopen button appear |
| Position | data-anchor-element-position | anchorPosition | beforebegin | beforebegin, afterbegin, beforeend or afterend |
| Email field | data-user-email-query-selector | userEmailSelector | | The form’s email input. See User tracking |
| Question field is a rich-text editor | data-main-input-formats-html | questionFieldIsHtml | Off | Turns the field’s HTML into text before sending |
A selector can be CSS, a name or an id. To reach inside an iframe or a shadow root, see Crossing DOM boundaries.
Start with Submit event. If the ticket is filed with no pause, switch to Click event. With Submit event the deflector listens on the form; with Click event, on the submit element.
What gets sent
| Setting | Attribute | Option | What it does |
|---|
| Extra inputs | data-extra-input-query-selectors and data-extra-input-names | extraFields | Other fields sent as context, such as a product dropdown. Comma-separated selectors and their labels, paired in order. The option takes [{ selector, label }] |
| data-deflect-subject-field | subjectField | The ticket subject, searched together with the question. Defaults to a field named subject |
| Answer only from these source groups | data-source-group-ids | sourceGroupIds | Answer only from these source groups. On the Behaviour tab |
data-extra-input-query-selectors="#request_subject,#request_product"
data-extra-input-names="Subject,Product area"
- An extra input reaches the answer as
Product area: Billing. It adds context but is not searched. A dropdown sends the label the reader sees.
- Up to 12 extra inputs are sent, each cut to 500 characters.
- Source groups set in the dashboard always win. The page can’t change them.
Deflection tracking
The Multi-step forms card on the Form wiring tab. See Multi-step forms.
| Setting | Attribute | Option | Default | What it does |
|---|
| Ticket-submitted element | data-deflection-failure-query-selector | failureSelector | Not set | The element that actually files the ticket. Once it is found, only it counts a ticket as filed anyway. See Multi-step forms |
| Watched event | data-deflection-failure-event-type | failureEvent | onSubmit | onSubmit or onClick |
Who asked
Script tag only, and shared with the Ask AI widget. See User tracking.
| Attribute | Option | Default | What it does |
|---|
data-user-id | userId | | Your own id for a signed-in reader |
data-user-email | userEmail | | The reader’s email, if the page knows it. A filled-in Email field wins |
data-user-analytics-cookie-enabled | anonymousId | true | Send an anonymous browser id |
data-user-analytics-fingerprint-enabled | fingerprint | false | Build the id from the browser instead of storing one |
Copy
The Copy tab. Leave a field empty to keep the default. Labels can be up to 120 characters, the notice up to 500.
| Setting | Attribute | Option | Default |
|---|
| Submit button text while armed | data-submit-button-text-override | submitButtonText | The page’s own label |
| Element holding the label | data-submit-button-text-query-selector | submitButtonTextSelector | The button |
| Notice shown before generating | data-pre-submit-info-text | preSubmitText | Nothing |
| Modal title | data-deflect-modal-title | modalTitle | We found an answer for you |
| Reopen button | data-modal-button-text | reopenText | View the answer again |
| While generating | data-modal-loading-text | loadingText | Understanding your issue… |
| | loadingSubText | Our AI assistant might have an answer for you in 10–15 seconds. No need to wait for a human reply if it helps. |
| Skip the wait | data-modal-skip-text | skipText | Skip and submit |
| Modal — satisfied | data-modal-dismiss-text | dismissText | That answered my question |
| Modal — file anyway | data-modal-submit-text | submitText | Continue to submit ticket |
| Follow-up placeholder | data-follow-up-placeholder | followUpPlaceholder | Ask a follow-up question... |
| Follow-up send button | data-follow-up-send-text | followUpSendText | Ask |
| Rating prompt | data-feedback-text | feedbackText | Did this solve your issue? |
| Rating — solved | data-feedback-yes-text | feedbackYesText | Yes, this solved it |
| Rating — not solved | data-feedback-no-text | feedbackNoText | No, I still need help |
- Submit button text while armed needs a Submit element or Submit button selector. It shows once the question is long enough to check, and the page’s own label returns after an answer is shown.
- Element holding the label is for a button with an icon: point it at the text inside (often
span) so the icon stays.
loadingSubText is the line under While generating, set from JavaScript only. An empty string hides it.
data-modal-title also works, but it sets the Ask AI widget’s title too.
When it runs
The Behaviour tab. The timings are under Timing.
| Setting | Attribute | Option | Default | What it does |
|---|
| Minimum question length | data-min-question-length | minQuestionLength | 25 | Shorter questions go straight through |
| Repeat window (seconds) | data-query-cache-seconds | cacheSeconds | 7200 | Submitting the same question again within this time files the ticket. -1 turns it off |
| Wait for form (ms) | data-wait-for-elements-timeout | waitForElementsMs | 10000 | How long to look for the form and question field. -1 waits forever |
| Delay before attaching (ms) | data-render-timeout | renderTimeoutMs | 0 | Wait before looking for the form. Try 500 for a form your framework re-renders after load |
| Hold back “skip” for (ms) | data-option-to-cancel-answer-delay | cancelAnswerDelayMs | 0 | Delay before Skip and submit appears |
| Attach as soon as the page loads | data-render-on-load | renderOnLoad | On | Off means you attach it yourself. See Rendering manually |
- Kelu also checks the minimum length against the dashboard value, so a lower value on the page won’t let shorter questions through.
- The repeat window is per browser and per question. An edited question is checked again. With
-1, every press asks again.
The modal
The Behaviour tab.
| Setting | Attribute | Option | Default | What it does |
|---|
| Allow follow-up questions | data-follow-up-enabled | followUp | On | A box under the answer for more questions, in the same conversation |
| Ask whether the answer helped | data-resolution-tracking-enabled | resolutionTracking | On | Yes/No buttons under each answer. Votes go to analytics. The reader never sees a count |
Hand-off
The Hand-off tab. See Conversation hand-off.
| Setting | Attribute | Option | Default |
|---|
| Carry the conversation onto the ticket | data-handoff-enabled | handoffEnabled | Off |
| Target field | data-handoff-target-query-selector | handoffTargetSelector | Required when hand-off is on |
| What to write | data-handoff-format | handoffFormat | link |
Debugging
| Attribute | Option | Default | What it does |
|---|
data-debug-mode | debug | false | Logs what the deflector found, decided and sent to the browser console |
JavaScript API
The script installs window.KeluFormDeflector:
window.KeluFormDeflector.attach({
knowledgeBaseId: "YOUR_KNOWLEDGE_BASE_ID",
clientKey: "kl_pk_...",
deflectorId: "YOUR_DEFLECTOR_ID", // loads every other setting
});
window.KeluFormDeflector.render(); // attach now
window.KeluFormDeflector.unmount(); // remove everything the deflector added
attach() takes any option from the tables above and replaces a deflector that is already attached. See Rendering manually.
Moving from kapa.ai? The deflector also reads kapa’s attribute names.