Platform feature. Requires
WANIWANI_API_KEY. Works whether your MCP server is self-hosted or on Managed Hosting. About the Platform.The taxonomy
The event set is closed and typed. Model every funnel stage with one of these; there is no custom event name.
Inside a flow node,
waniwani comes from the handler context and already carries the session identity. Full type shapes are in the Event schema.
Placing lead_qualified
lead_qualified is your code declaring that a person met your qualification bar: they finished the qualifying questions, requested a demo, or matched your target profile. Three rules:
- It fires at the qualification bar, not at flow entry. Starting a flow is activity (
tool.calledcovers it). The event belongs in the node where qualification completes, which is often the node that pushes the lead to your CRM. - It fires once per flow run.
- Fill every property you can. The properties are the join keys the dashboard uses to dedupe leads and connect them to your system.
Sharing an email mid-conversation is
identify(userId, { email }), not a qualified lead. identify attaches identity; lead_qualified declares that your bar was met. Most flows emit both, at different nodes.Placement rules for the rest
identifyas early as possible. The first node where an email or user id is in state callswaniwani?.identify(state.email). This is what lets an off-platformconvertedfind its way back to the session.- Price events go where the numbers are. The node that computes a single price emits
priceShown; the node that presents multiple plans emitspricesCompared; the node that runs after the user picked (the selection is now in state) emitsoptionSelected. convertedonly on real conversion. A confirmed booking or completed purchase inside the flow. If the sale closes later on your own site, emit it from your backend with the sameexternalUserIdyou identified during the flow. See Identify.- Emit from node handlers, never from
validatecallbacks. Validation can run several times per answer and would duplicate events. - Always guard with
waniwani?.. The scoped client isundefinedwhen the server is not wrapped withwithWaniwani(server), and tracking throws without an API key. Optional chaining keeps keyless runs working. - Never pass
sessionIdmanually inside a flow. The scoped client carries it.
A fully instrumented flow
lead_qualified; a support flow may only ever identify. Forcing events onto nodes that do not represent that stage corrupts the funnel.
Auto-instrument with the skill
Theinstrument-tracking agent skill applies everything on this page to your codebase: it inventories your flows, maps every node to the taxonomy, inserts guarded tracking calls with metadata read from your flow state, wraps the server with withWaniwani if needed, and runs your typecheck.
- “Instrument tracking across my flows”
- “Track qualified leads in my demo flow”
- “Add conversion tracking to the booking flow”
Checklist
- Exactly one
leadQualifiedper flow run, placed at the qualification bar, withexternalIdwhen your CRM returns one. identifyruns at the earliest node where a stable id exists.- Every tracking call is guarded with
waniwani?.and none passessessionId. withWaniwani(server)wraps the server.- Off-platform conversions send
convertedfrom your backend with the identifiedexternalUserId.