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.
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
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.
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.