Skip to main content
DocsEmbed scriptEvents
Embed script

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 load config.json.
  • scenarios — couldn't load scenarios.json.
  • vectors — couldn't load vectors.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,
  })
})
Don't replay messages back to the visitor
Events are read-only signals. You can record them, log them, or react in your own UI — do not try to inject text back into the chat using the DOM. Subscribe to content, drive intent via reactive properties.