Skip to main content
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

Under the hood it calls 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

A typical pattern is to call 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:
This produces one event instead of two.

Inside tools and flows

When the server is wrapped with withWaniwani(), 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:
See Widgets & chat.

Anonymous vs. identified sessions

Sessions start anonymous. They get a sessionId 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.
Calling identify() multiple times with the same userId is fine. Each call is an independent user.identified event.