Skip to main content
The chat widget can mirror its lifecycle events into the host page through a single onEvent callback. Both hosted surfaces accept it: WaniWani.chat.init({ onEvent }) on the script embed, and the onEvent prop on the React component. The embed mounts in your page’s DOM (the shadow root isolates styles only), so the callback runs in your page’s own JS context. Forward events straight into window.amplitude, window.analytics, or gtag and your page-side analytics SDK attaches its own user identity automatically — no server-side identity plumbing, and no Amplitude/Segment SDK is ever loaded inside the widget.
When to use which channel. onEvent mirrors widget activity into your analytics on the host page — it never leaves the browser unless your callback sends it somewhere. It is separate from platform ingest tracking (WaniWani.chat.track, funnel events, the typed event catalog), which sends events to the Waniwani platform for funnel analytics. Use onEvent to see chat activity next to your existing product analytics; use tracking events to power Waniwani funnels. The two channels are independent and can be used together.

The events

Do not assume chat.opened precedes the first message.sent: a send from the docked bar opens the panel and sends in one action, and the open transition is reported after the send.
Separate from the track() taxonomy. Some onEvent names (session.started, link.clicked) also exist as event names in the hosted track() funnel taxonomy — same concepts, but a separate client-side channel with different payload shapes. onEvent events are not client.track() events, do not feed the platform funnel dashboard, and should not be forwarded into client.track() as-is even where names coincide.

The payload

Every callback receives a WidgetEvent — exported from @waniwani/sdk/chat alongside WidgetEventName and WidgetMode:
mode is the embed surface: "floating" for the floating bar, "inline" for an in-page mount. <WaniwaniChat> reports "inline" — it is an in-page mount, and this is the same mode tag its server-side events (chat requests, page.viewed) carry, so both streams correlate. Narrowing on name types properties automatically:
Event names are neutral and fixed — the widget never adopts your analytics naming. Map them to your own schema inside the callback.

Script embed → Amplitude

onEvent is programmatic-only on the script embed: a function cannot be expressed as a data-* attribute, so pass it to WaniWani.chat.init().
Because the Amplitude snippet on your page already identified the user, every mirrored event lands in Amplitude under that same identity.

React → Segment

The WaniwaniChat component takes the same callback as an onEvent prop (chat.opened/chat.closed are floating-embed-only and never fire here):

Privacy

Message content never passes through this channel. message.sent and message.received tell the host that a message was exchanged, never what was said. The one text field that does flow through is suggestion.clicked’s text — suggestion pills are operator-authored config, not user input.

Error isolation

Exceptions thrown by your callback are swallowed and logged as a console warning; they never break the widget or block other subscribers. Still, keep the callback fast — it runs synchronously on the widget’s event path.
ChatEmbed, the bring-your-own-backend React primitive, does not expose onEvent. Like track/identify, it is a hosted-surface feature of WaniwaniChat and the script embed.