Events
The custom events the embed fires on <zupport-chat> — ready, message, error, tier-change.
Last updated 2026-06-03
The embed dispatches custom events on the <zupport-chat> element. They bubble and are composed, so you can listen on document or any ancestor.
zupport-ready
Fires once the engine has booted and the widget is ready to accept input.
chat.addEventListener('zupport-ready', (e) => {
console.log('engine:', e.detail.engine) // 'llm' | 'scenarios'
console.log('mode:', e.detail.mode) // 'mobile' | 'desktop'
})zupport-message
Fires whenever a message is sent or received. The same event fires multiple times for a streaming assistant reply (status streaming per chunk, then done).
chat.addEventListener('zupport-message', (e) => {
console.log(e.detail.role) // 'user' | 'assistant'
console.log(e.detail.text) // message body
console.log(e.detail.source) // 'scenario' | 'llm' | 'fallback' (assistant only)
console.log(e.detail.status) // 'streaming' | 'done' | 'error'
})Detail payload:
role—'user'for input,'assistant'for reply.text— full message body so far (cumulative for streaming).source— only set for assistant replies.'scenario'when a curated answer matched,'llm'when the AI fallback ran,'fallback'when neither engine could answer.status—'streaming'for partial chunks,'done'on completion,'error'if generation failed.
zupport-error
Fires when something goes wrong during boot or while generating a reply. The widget remains in the DOM but may be in a degraded state.
chat.addEventListener('zupport-error', (e) => {
console.warn(e.detail.phase) // 'config' | 'scenarios' | 'vectors' | 'engine'
console.warn(e.detail.error) // Error or string
})Phases:
config— couldn't loadconfig.json.scenarios— couldn't loadscenarios.json.vectors— couldn't loadvectors.json.engine— model failed to initialise or generate.
zupport-tier-change
Fires when the active mode or engine changes — either at boot, when the visitor flips the preview toggle, or when the LLM engine fails to start and the widget downgrades to scenarios.
chat.addEventListener('zupport-tier-change', (e) => {
console.log('engine:', e.detail.engine) // 'llm' | 'scenarios'
console.log('mode:', e.detail.mode) // 'mobile' | 'desktop'
console.log('reason:', e.detail.reason) // e.g. 'engine-init-failed'
})Reasons:
engine-init-failed— the LLM couldn't start; the widget fell back to scenarios.mode-changed— preview toggle switched between desktop and mobile.null— the very first runtime selection at boot.
Cleaning up listeners
Always remove listeners in framework lifecycle hooks to avoid leaks. Examples are on the Frameworks page.
Worked example: send to your analytics
Track every user-sent question without ever seeing the reply text:
chat.addEventListener('zupport-message', (e) => {
if (e.detail.role !== 'user' || e.detail.status !== 'done') return
myAnalytics.track('zupport_question', {
length: e.detail.text.length,
})
})