Skip to main content
Platform feature. The browser surfaces authenticate with credentials they already hold: a widget token injected by withWaniwani, or the chat widget’s public wwp_... token. No API key ships to the browser. About the Platform.
Widgets are where funnel steps actually happen: the user compares plans, picks an option, clicks the buy link. Both browser surfaces expose the same track client as the server, with session identity attached automatically.

MCP-app widgets: useWaniwani()

For widgets rendered inside tool responses (ChatGPT apps, Claude MCP apps). Requirements: the MCP server is wrapped with withWaniwani(server), which injects the tracking endpoint, a short-lived widget token, and the session id into each tool response under _meta["waniwani/widget"]. useWaniwani() is host-agnostic: it takes that metadata as data and opens no connection to the widget host itself. How you hand it the metadata depends on your host. skybridge-hosted widgets. Import the hook from the skybridge adapter entry and call it bare. It reads skybridge’s useToolInfo().responseMetadata and feeds it to the hook for you:
skybridge is an optional peer dependency of the SDK; a skybridge project already has it. Any other host. Import from @waniwani/sdk/mcp/react and pass the tool-response _meta your host exposes as toolResponseMetadata:
The returned widget surface: One widget_render event is emitted automatically when the hook initializes with resolved config, so “widget shown” exists as a funnel step even if you track nothing manually. With no resolved endpoint and source (no metadata, no explicit options), the hook returns a no-op widget, so rendering never breaks.

Explicit configuration

When the widget cannot resolve config from the host (or you proxy events through your own backend), pass the endpoint directly. source is required in this mode so events attribute to a channel:
Pair this with createTrackingRoute on your backend when you want zero Waniwani credentials in the browser.

Chat host pages: chat.track

The chat embed and WaniwaniChat hold the public token and the server-assigned session, so the host page can send funnel events with zero setup. Events carry the sessionId once the first exchange assigns one, and the anonymous visitorId before that. With the <script> embed:
With the React component, the same surface lives on the ref handle:
track and identify are absent on the bare ChatEmbed primitive, which is bring-your-own-backend and holds no Waniwani credential. To mirror chat lifecycle events (opened, closed, message sent/received) into your own analytics instead, use the embed’s onEvent callback; it runs in the page’s JS context and carries the sessionId.

Custom surfaces: createFrontendClient

Both surfaces above are wrappers around one primitive, exported for anything they do not cover:
It shares the server client’s mapper and batching transport, adds keepalive flushing on page hide/unload, and reads identity() at emit time so late-arriving ids (a session assigned mid-page) are picked up without recreating the client.

Delivery behavior

  • Events batch in memory and flush on a short timer, on batch size, and with keepalive requests when the page is hidden or unloading.
  • Tracking never throws into the host page; failures are logged and dropped.
  • On an auth failure (expired widget token, revoked public token) the transport stops and logs the reason once, instead of retrying forever.