Platform feature. Requires
WANIWANI_API_KEY on the server. About the Platform.sessionId, and on every SDK surface except the top-level backend client that id attaches to your events automatically. You read it when you need to link a session to your own systems; you almost never pass it.
Identity: three ids, any one will do
Every event is attributed by one or more of three correlation ids. The ingest API accepts an event that carries any one of them and rejects an event that carries none:
You rarely choose between them. Each host provides whatever identity it natively has, the SDK maps it for you, and you read whichever is present:
visitorId exists only on the web, because it is a browser-side construct; the chat embed sends it on every request, so a web event is always attributable even before its first message mints a session. On ChatGPT and Claude there is no visitorId — the host’s own session (and, for ChatGPT, an anonymous user id) carries attribution instead. The practical rule for reading identity in server code: waniwani.visitorId ?? waniwani.externalUserId ?? waniwani.sessionId — take whatever the current host gives you.
Where the session id comes from
Inside
_meta, the SDK reads the first non-empty string among waniwani/sessionId, openai/sessionId, openai/session, sessionId, conversationId, and mcp-session-id. When the host only carries a transport-level session (the Mcp-Session-Id header), withWaniwani promotes it into _meta.waniwani/sessionId so everything downstream sees it. You do not deal with any of this directly.
Reading the ids
Every surface exposes the resolved ids so you can persist them and attribute later activity back to the conversation. In a flow node or tool handler the scoped client exposes all three — read whichever the host provides:<script> embed’s onEvent callback also carries sessionId on every lifecycle event, which is the easiest way to mirror it into your own analytics.
Linking back to a session later
The point of reading the id: a conversion that happens days later, outside the conversation, can still land in the right session. Store the id during the conversation, then send the event from your backend with it:externalUserId works the same way when you identified the user instead of storing the session (see Identify). Use whichever id your system already keeps.
Anonymous visitors
The webvisitorId is a stable, opaque id persisted in the browser (localStorage), minted synchronously on first use so it is present on the very first event or chat request — no async, no dependency on a secure context. page.viewed carries only a visitorId, deliberately, so landings do not mint sessions and the “landed vs. started a conversation” funnel stays meaningful.
Bring your own visitor id
Override the auto-generated id with one your site already tracks (a PostHog / Amplitude / Segment distinct id, your own cookie). Waniwani then correlates to the same visitor your analytics sees, and your server code reads the id back aswaniwani.visitorId, so a flow or tool can push events to the same analytics tool the id came from. On the <script> embed, set data-visitor-id or call WaniWani.chat.setVisitorId(id) once your analytics SDK is ready; with the React component, pass the visitorId prop. Full recipes: Link visitors to your analytics.
Manual session ids
For background jobs, backfills, or tests where no surface provides an id, setsessionId explicitly:
Session lifecycle
The Waniwani backend emitssession.started the first time it sees a new sessionId. You never send it yourself. Sessions have no hard close; the backend treats them as closed after an idle window. A returning user either resumes the same session (within the window) or starts a fresh one, depending on what the host client sends.