app.waniwani.ai, so no backend proxy is required on your side.
Platform feature. The dashboard generates the embed snippet for your project. Open app.waniwani.ai to get yours, or read more about the Platform.
mode: "inline" (default) mounts the chat wherever you place a <div data-waniwani-embed></div> marker, sized by your CSS on that container; mode: "floating" renders a docked bar that expands into a full chat panel.
Quick start
- Open your project on app.waniwani.ai and copy the chat embed snippet from the project settings.
- Paste it into your site’s HTML, alongside a mount point where you want the chat to appear:
Sizing
The embed fills its container. Give the<div data-waniwani-embed> a definite size via CSS:
ResizeObserver mirrors the container’s max-height onto the embed so padding and borders are respected.
Overriding attributes
The snippet you paste is a starting point. After pasting, you can editdata-* attributes on the <script> tag to tweak behavior:
Leave the wiring attributes (
data-token, data-channel-id, data-api, the script src) as the dashboard generated them.
Colors, fonts, and bubble shapes are not set through data-* attributes — set --ww-* CSS variables on the container instead. See Theming & customization for the full variable list and deep-customization recipes.
Full type definitions: EmbedConfig and ChatTheme.
Programmatic init
For runtime control (computing options dynamically, mounting on demand), drop thedata-token attribute from the script tag and call init() manually. You still need the wiring attributes from a dashboard-issued snippet:
init() returns an instance with destroy() and sendMessage(text). You can also call window.WaniWani.chat.destroy() and window.WaniWani.chat.sendMessage("...") directly on the global.
init() also accepts an onEvent callback that mirrors chat lifecycle events (opens, messages, errors, link clicks) into your own analytics — Amplitude, Segment, gtag — with your page’s identity attached automatically. See Widget events (onEvent).
Link visitors to your analytics
By default the embed mints its own anonymous visitor id, a stable opaque value persisted in the browser’slocalStorage. It rides on every chat request and tracking event, so a visit is attributable before the first message mints a session (see Sessions).
If your site already tracks visitors with PostHog, Amplitude, Segment, or a first-party cookie, override that id with your own. Waniwani then correlates its sessions and events to the same visitor you see in your analytics, and your server-side MCP tools and flows read the id back as context.waniwani.visitorId, so they can send events straight to the same analytics tool the id came from.
There are three ways to set it. Pick the one that matches when your id is available.
If you know the id up front
Set it declaratively on the script tag:init() when you initialize programmatically. init() accepts a string or a resolver (sync or async), so you can read the id inline:
If the id resolves asynchronously (the common case)
Analytics SDKs usually assign a distinct id only after they bootstrap, so read it when it’s ready and hand it to the widget.setVisitorId() is safe to call at any time, before or after init(), and the new id applies to the next chat request and tracking event:
window.WaniWani.chat.getVisitorId().
The visitor id identifies a device / browser, not a signed-in account. When a visitor logs in, keep the visitor id and additionally call
identify() with your account id, so anonymous and known activity stitch together.Self-hosting the JS bundle
If you’d rather not load from a CDN, pin a specific SDK version and serve the bundled file yourself. Thedata-* attributes still come from the dashboard snippet — you’re only swapping where embed.js is served from:
How it works
- The script reads its own
data-*attributes (token, channel ID, theming, content overrides). - On
DOMContentLoaded(or immediately, if the DOM is already ready), it locates[data-waniwani-embed]and mounts the chat inside it via Shadow DOM. - The chat calls
app.waniwani.ai/api/mcp/chatwith the project token and streams responses back through Server-Sent Events. - Conversation history lives only in memory by default; set
data-enable-thread-historyto persist threads in IndexedDB on the user’s device.