Platform feature. Requires
WANIWANI_API_KEY on the server. Browser surfaces authenticate with a public token (wwp_...) or an injected widget token instead. About the Platform.track surface that is identical everywhere it appears:
track exists on four surfaces, each of which attaches session identity for you:
The first three need no identity plumbing at all. Only the top-level backend client requires you to say who the event belongs to. See Sessions for the identity model and Widgets for the browser surfaces.
The event catalog
TheEventType union is a closed, typed set, exported at runtime as EVENT_TYPES from @waniwani/sdk. These are all the events:
Type shapes for every property interface live in the event schema reference. Placement guidance (which event belongs at which flow node) lives in Instrumentation.
Any event name outside this table is rejected: the
TrackEvent union will not typecheck it, and the ingest API does not power funnels with it. Model your funnel with the events above instead of inventing names.
What counts as a qualified lead
lead_qualified means the conversation produced a concrete handoff toward your business. A lead is qualified whenever the user:
- is redirected to your pricing page or to your own website,
- books a call or a meeting,
- is sent an email (a quote recap, a follow-up, a booking confirmation),
- or otherwise meets an explicit qualification bar you defined (finished the qualifying questions, requested a demo, matched your target profile).
identify(): that attaches identity to the session and is the join key for later attribution, but it does not mean the lead is qualified yet. identify says “we know who this is”; lead_qualified says “this person is worth your sales attention”. Most funnels emit both, at different moments. See Identify and Instrumentation for placement.
The chat backend also logs chat.user_message and chat.assistant_message server-side for every exchange; you never send those from code.
Everything on this page is platform ingest: events sent to Waniwani to power funnel analytics. The chat widget also offers a separate host-callback channel — onEvent — that mirrors widget lifecycle events into your own page-side analytics (Amplitude, Segment, gtag) without touching the Waniwani ingest. See Widget events (onEvent).
Revenue helpers
The five revenue funnel events have flat typed helpers ontrack (there is no track.revenue.* namespace). Each accepts its event’s properties plus the shared tracking context (sessionId, externalUserId, visitorId, meta).
occurredAt on converted backdates an off-platform sale to when it actually happened; the event itself may arrive months later.
Sending events
From a tool handler or flow node
Wrap the server once withwithWaniwani(server), then use the scoped client from the handler context. Session identity is already attached; you pass nothing.
waniwani.sessionId. Store it with your own records (a lead, an order) and you can attribute an off-platform event back to the conversation later.
From a widget or the chat embed
The browser surfaces expose the sametrack. See Widgets for setup; the calls are identical:
From your backend
Create one top-level client (for example inlib/waniwani.ts) and pass identity explicitly on every call. This is how an off-platform conversion finds its original lead:
sessionId during the conversation, sessionId: storedSessionId works as the join key too.
The identity rule
Every event must carry asessionId, an externalUserId, or a visitorId. The ingest API rejects events with none, and the SDK warns at enqueue time. The scoped client, the widget hook, and the chat embed satisfy this automatically; the top-level backend client is the one place you pass identity yourself.
Return value
eventId is stable and safe to log. It is assigned before the envelope leaves the process, so logging it tells you an event was accepted into the buffer, not that it reached the backend. track() returns as soon as the envelope is enqueued, before the network request completes.
Flushing
The transport flushes on a timer and on batch size. In long-running processes you do not need to callflush() yourself. In serverless functions, call it before the function returns:
keepalive requests when the page is hidden or unloading, so events survive navigation.