Lifecycle
Anonymous, identified, logout, user switch. What to call at each transition.
Single-page apps cycle through identity states without reloading. Each transition maps to one call.
Cold visitor, no auth
const widget = mount({ widgetId: 'WIDGET_ID' });The widget boots anonymously: the visitor gets a stable ID, and their conversations are restored on later visits as long as consent allows the persistent cookie.
Visitor signed in at first paint
const widget = mount({
widgetId: 'WIDGET_ID',
identity: { email: user.email, userHash: user.userHash },
});The identity is sent with the /widget/boot request, so the session is attached to the verified contact before first paint. No identified event fires on this path (that event acknowledges identify() calls); listen for ready if you need a signal that boot completed. One exception: when identity includes customAttributes, those are delivered through a follow-up identify() after boot, which does emit identified.
In SPAs the identity often isn't known until after hydration. Use autoload: false:
const widget = mount({ widgetId: 'WIDGET_ID', autoload: false });
auth.onReady(({ user, userHash }) => {
if (user) {
widget.user.identify({ email: user.email, userHash });
}
widget.load();
});See Assistify SDK → Deferred boot.
Sign-in mid-session
auth.onSignIn(({ user, userHash }) => {
widget.user.identify({ email: user.email, userHash });
});If the visitor was anonymous and had already started a conversation, the transcript is attached to the matching contact server-side. This attachment requires the signed userHash; an unsigned claim stays on an isolated contact instead (see Identity verification). The identified event reports merged: true when the transcript was attached to an existing contact.
widget.events.on('identified', ({ verified, merged }) => {
if (merged) console.log('Anonymous session attached to existing contact');
});Logout
auth.onSignOut(() => {
widget.reset();
});reset() wipes the session locally and server-side, then re-boots anonymous; the full list of what it clears is under JavaScript API → reset(). Listen for ready to know the post-reset boot finished.
Skip this and the next visitor on a shared browser inherits the previous conversation history.
User switch on the same device
auth.onUserSwitch(({ user, userHash }) => {
widget.reset();
widget.user.identify({ email: user.email, userHash });
});reset() first; otherwise the new identity gets shallow-merged onto the previous one, leaving leftover fields from the old user.
Back-to-back calls are safe: the identify() is held until the post-reset session boots, so it applies to the new visitor rather than the one being torn down. You don't need to wait for the ready event between the two calls.
Page reload
Nothing to call. mount() runs again on the new page, the cookie identifies the returning visitor, and the server restores the conversation list.
Recap
| Transition | Call |
|---|---|
| Cold visitor | mount({ widgetId }) |
| Verified at first paint | mount({ widgetId, identity }) |
| Deferred boot | mount({ widgetId, autoload: false }) then widget.load() |
| Sign-in mid-session | widget.user.identify({ email, userHash }) |
| Logout | widget.reset() |
| User switch | widget.reset() then widget.user.identify(...) |
| Page reload | mount(...) |