Every option has three spellings that mean the same thing: a field on the deflector in the dashboard, a data-* attribute on the script tag, and a camelCase key passed to attachFormDeflector(). Precedence is fixed: the page wins. The stored configuration is the default for every page carrying that deflector id; an attribute on one page is a deliberate exception to it, and an exception that loses to a default is not an exception.
Booleans are true unless the attribute value is exactly "false" or "0". An absent attribute is not false — it leaves the documented default in place, which matters for the several options that default to on. An empty string means “leave the default”.

Required

The submit trigger

Start with onSubmit. A form that posts fires it, and the listener sits on the form itself where the event is actually dispatched. Switch to onClick when the form does its work in a click handler and never fires submit at all — which is most React and Vue support portals.
A submit listener on a button never fires: submit is dispatched at the form. Pointing submitSelector at the button while leaving the event on onSubmit silently intercepts nothing. The event decides which element gets the listener; the selector only says which button is yours.
submitButtonTextSelector exists because a themed button is usually <button><svg/><span>Submit</span></button>, and writing the new label onto the button replaces the icon along with the words. Point it at the span. It is resolved inside the button first, so span is enough.

Where the deflector’s own elements go

The inline strip is three things at three moments, all at the same anchor: the notice before anything happens, the progress line while an answer is generated, and the button that reopens an answer the reader closed. They are torn down together, which is what lets unmount() promise the form is left exactly as it was found.

What gets sent

The label is not decoration. The value reaches the model as Product area: Billing, and Billing on its own is a word with no job. A <select> sends its visible label rather than its value — 3 tells a model nothing. Extra fields are fenced into the prompt as context, never instructions, and they are not added to the retrieval query: “Billing” in a dropdown says which docs to prefer, and pasting it into the query would send retrieval chasing the word itself. Twelve fields maximum.
A deflector configured with source groups applies them whatever the page asked for, and the page cannot widen the set. The embed is public source; the deflector row is not.

Deflection tracking

Only needed on a multi-step form, where the button that starts the answer is not the button that files the ticket. Without them every deflection on such a form is recorded as a success. Also accepted under their longer names, data-deflection-failure-trigger-element-query-selector and data-deflection-failure-trigger-element-event-type.

Who asked

The email is read at submission time and stored on the conversation, so a deflection can be joined to the ticket that arrived anyway. Anonymous without it. The last two are the same attributes the Ask AI widget reads, so one signed-in reader is one reader on both surfaces. They are page-level only: the deflector row is shared by every page carrying its id, and who is signed in is a fact about one of them. See User tracking for exactly what is kept.

Copy

Every string the reader sees is configurable, because the modal renders inside your support portal — in its voice, and often not in English.
data-modal-title without the deflect- prefix is shared with the Ask AI widget’s own header, which is usually right — same product, same name. Use data-deflect-modal-title to say otherwise on a page carrying both. data-modal-go-back-text is accepted as a synonym for data-modal-submit-text.

When it runs

The length floor is enforced on both sides. “it’s broken” retrieves nothing, and an answer to it is worse than no answer — it spends a completion and teaches the reader to skip the modal. The widget declines to ask; the server declines to answer, because the widget is a script on someone else’s page. The repeat window is what makes the second press file the ticket. A reader who read the answer, closed the modal and pressed submit again has made their decision; re-answering costs a completion and delays the ticket they chose to file. It is keyed on the question text, so an edited question is a different question and gets a fresh answer. Waiting for the form and delaying the attach are different things. waitForElementsMs polls until the elements exist — for a form behind a dropdown or a route change. renderTimeoutMs waits before looking at all, for a server-rendered form whose framework replaces the markup on hydration: polling cannot see that, because the elements it is waiting for are already there and about to be discarded. Try 500 if the deflector attaches and then goes quiet.

The modal

The block reads Did this solve your issue? with two labelled buttons rather than bare thumbs — the reader is mid-decision about whether to file a ticket, and a thumb beside an answer is ambiguous with rating the writing. Ratings show no count to the reader. A vote is a signal for you, in Analytics, not a poll the next reader sees. Pressing the lit thumb withdraws it; pressing the other replaces it. Follow-up answers are rated the same way — and usually matter more, since the follow-up is the turn the reader stayed for.
The deflection rate cannot stand in for this. It counts tickets avoided, and a confidently wrong answer avoids the ticket too. Turning ratings off leaves you with no per-answer quality signal on the one surface where being wrong costs most.

Hand-off

See Conversation hand-off.

Diagnostics

JavaScript API

unmount() removes every listener and every element the deflector added, leaving the form exactly as it was found. That is what makes conditional deflection possible — see Rendering manually.