@waniwani/sdk/chat. Pick based on who runs the chat backend:
The React components mount inline: they render wherever you place them in your layout, and you control the container. There is no launcher or floating bubble in the React path — for that, use the embed script in
floating mode.
Platform feature. Requires a Waniwani project token (
wwp_...). Get one at app.waniwani.ai or read more about the Platform.Install
_app.tsx or your root layout):
WaniwaniChat
The hosted chat. Pass your project token and channel ID and it talks toapp.waniwani.ai directly — no API route, no proxy, no backend work on your side.
overrides.appearance.
Sizing and placement
WaniwaniChat fills its parent. Put it wherever you want it and size the container:
className is applied to the root element if you’d rather style it directly.
Per-page overrides
The dashboard is the source of truth. Useoverrides only for a local tweak that doesn’t justify a separate channel:
Tracking from the host page
Theref exposes a ChatHandle with track and identify alongside the chat controls. identify takes the user id as its first argument, with optional traits second — it is not an object:
WaniwaniChat only — the bare ChatEmbed primitive has no Waniwani credential to send events with. See Identify.
ChatEmbed
A bare-bones primitive for when you run the chat backend yourself.api is required and there is no default; pass any endpoint implementing the Vercel AI SDK chat protocol and the component handles the rest.
Reach for
ChatEmbed only when self-hosting. If you’re using the Waniwani Platform, WaniwaniChat gives you the same UI plus dashboard configuration, visitor correlation, and tracking.Link visitors to your analytics
Both components accept avisitorId prop to override the anonymous id the widget generates with one you already track (a PostHog / Amplitude / Segment distinct id, your own cookie). Waniwani correlates its sessions and events to the same visitor your analytics sees, and your server-side MCP tools and flows read it back as context.waniwani.visitorId. Leave it unset to keep the auto-generated, localStorage-persisted id.
visitorId accepts a string, or a resolver that returns one. The resolver may be sync or async, which is handy when your analytics SDK only exposes its id after it bootstraps:
Attachments
ChatEmbed takes the documents module’s state as a prop, since it fetches no remote config of its own:
enabled is true, and the upload endpoint enforces the same flag. WaniwaniChat and the script embed read all of this from the dashboard, so they need none of it. See Documents for what an agent does with the files.
Authentication
headers goes out with every chat request, and document uploads reuse it, so one bearer usually covers both.
uploadHeaders when your upload endpoint takes a different credential than your chat endpoint. It is merged over headers on the document upload and discard calls, and leaves the chat request alone.
X-Tenant-Id. Only the names you repeat are replaced, and the match ignores case, so authorization overrides Authorization.
Both land in the browser, so put only public credentials in them.
Theming
Both components take an appearance preset (light, dark, or auto) with optional per-property overrides layered on top:
auto follows the host’s prefers-color-scheme. variables accepts every ChatTheme token — colors, message-bubble radius/padding/max-width, base font size and line height. The full token and CSS-variable list lives on Theming & customization.
Deep customization
Two escape hatches when tokens aren’t enough:appearance.assistantBubble— opt-in filled bubble for assistant replies (plain text by default), styled byassistantBubbleColor/assistantBubbleTextColor.classNames— per-slot class overrides merged onto the widget’s own classes:root,header,message,userBubble,assistantBubble,input. Pass it directly onChatEmbed, or asoverrides.classNamesonWaniwaniChat.
Mirroring events into your analytics
The hostedWaniwaniChat component accepts an onEvent prop that fires on chat lifecycle events (messages, session start, errors, link clicks) so you can mirror widget activity into your own analytics — Segment, Amplitude, gtag — with the page’s identity attached automatically. ChatEmbed, the bring-your-own-backend primitive, does not expose it. See Widget events (onEvent).