Platform feature. Requires
WANIWANI_API_KEY. Works whether your MCP server is self-hosted or on Managed Hosting. About the Platform.identify() sends a user.identified event and attaches an externalUserId to the current session’s correlation ids. Use it as soon as you know who the user is: they shared an email, a phone number, a first and last name, or your system knows a stable id (a database id, a CRM record id, a Stripe customer id).
Identified is not qualified. Learning an email, a phone number, or a name means
identify(); it does not make the lead qualified yet. lead_qualified fires separately, when the conversation produces a concrete handoff toward your business (a redirect to your pricing page or website, a booked call, an email sent to the user). See what counts as a qualified lead.Signature
track() with event: "user.identified" and passes userId as externalUserId. The call returns { eventId } immediately, the same as track().
string
required
A stable external identifier for the user. Database id, CRM record id, Stripe customer id, or similar. Emails work but are not ideal because they change.
Record<string, unknown>
User-level traits (name, email, plan, signup date, and so on). These land in the event
properties payload.Record<string, unknown>
MCP request metadata, typically
extra._meta. Required so the identify call can be correlated with the current session.Usage
identify() from the tool that first learns the user’s identity:
Identify inline with track()
If the same tool call both learns the identity and produces a domain event, pass externalUserId on the track() call instead of making a separate identify() call:
Inside tools and flows
When the server is wrapped withwithWaniwani(), the scoped client on waniwani in flow nodes (or via extractScopedClient(extra) in a hand-registered tool) already has the request’s _meta attached, so identify() takes only userId and optional properties:
From the browser
The browser surfaces expose the same call with identity plumbing built in. In an MCP-app widget,useWaniwani().identify() stamps the user id on every later widget event; on a chat host page, WaniWani.chat.identify() (or ChatHandle.identify) ties the visitor and chat session to your user id:
Anonymous vs. identified sessions
Sessions start anonymous. They get asessionId from _meta but no externalUserId. When you call identify() (or pass externalUserId on a track() call), future events in the session carry the user id. How the backend back-fills earlier events in the same session for a newly identified user is a server-side concern, not something the SDK controls.