Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/jAtInn71/chatwoot-costom/llms.txt

Use this file to discover all available pages before exploring further.

Session management in this fork covers the full lifecycle of a visitor’s identity and conversation inside the widget: how a new contact is created when a visitor first opens the chat, how the session persists as the visitor navigates between pages, and how it is cleanly reset when the conversation ends — either because the visitor clicks “Exit Chat” or because the dashboard resolves the conversation remotely. The central design constraint is that the iframe must never be reloaded during a reset. A full iframe reload causes a white flash visible to the visitor and breaks any host page code that listens for the exitChat postMessage event. All exit paths in this fork perform a “soft exit” instead.

Contact creation and auth token

When the widget loads and no existing session is found in storage, custom/widget/api/contacts.js creates a new anonymous contact by calling GET /api/v1/widget/contact?website_token=…. The server responds with a widget_auth_token that is saved to localStorage and set as the X-Auth-Token request header for all subsequent API calls. The WEBSITE_TOKEN used in every API URL is captured once at module load time from the original iframe URL query string. It is intentionally not cleared during exit because it is configuration data (which inbox this widget belongs to), not session data. Clearing it would cause website_token = null errors on every API call after a reset.
// custom/widget/api/contacts.js:15-19
const buildContactUrl = endpoint => {
  const base = `/api/v1/${endpoint}`;
  if (!WEBSITE_TOKEN) return base;
  return `${base}?website_token=${WEBSITE_TOKEN}`;
};
On subsequent loads (page refresh, navigation), App.vue rehydrates the session by calling fetchOldConversations and getAttributes, which resume the existing conversation without showing the pre-chat form again.

Soft exit flow

All three exit triggers — user clicking “Exit Chat”, the dashboard resolving the conversation, and the auto-resolve inactivity timer — converge on the same four-step sequence:
1

Resolve the conversation server-side

toggleStatus is called to set the conversation status to resolved via GET /api/v1/widget/conversations/toggle_status. This step only runs for the user-initiated exit path; auto-resolve and dashboard-initiated closes have already resolved the conversation before the widget detects them.
2

Dispatch contacts/softExitChat

contacts/softExitChat (custom/widget/store/modules/contacts.js:281) resets Vuex contact state to a blank user object, removes the X-Auth-Token, api_access_token, and user_access_token axios headers, and calls clearSessionStorage().Critically, it does not post { event: 'exitChat' } to the parent and does not call window.location.reload(). The iframe stays alive.clearSessionStorage removes session keys from both localStorage and sessionStorage, sweeps any remaining cw_* / cwc* / widget_auth* keys, expires matching cookies, and strips session params from the iframe URL — all while leaving chatwoot_user_data intact so the pre-chat form can be pre-filled on the next session.
3

Navigate the router to home

router.replace({ name: 'home' }) is called immediately after the Vuex reset. The next paint renders the team-availability home view. Because conversationSize is now 0, the home view will route to the pre-chat form when the visitor starts a new conversation.
4

Collapse the iframe panel

IFrameHelper.sendMessage({ event: 'closeWindow' }) posts a message to the parent SDK, which collapses the iframe panel and shows the chat bubble. The visitor can reopen the bubble to start a fresh conversation.

Code touchpoints

FileWhat it does
custom/widget/store/modules/contacts.js:281softExitChatClears Vuex state, auth headers, and session storage
custom/widget/components/HeaderActions.vue:91endChatUser-initiated exit: calls toggleStatus, then softExitChat, then closeWindow
custom/widget/views/App.vue:374checkAndClearResolvedConversationDashboard/auto-resolve path: detects resolved status, calls softExitChat, then closeWindow

Auto-resolve detection (polling)

App.vue polls the conversation status to detect when the dashboard or an inactivity timer has resolved the conversation externally. The polling logic is intentionally lightweight:
// custom/widget/views/App.vue:362-372
startConversationStatusCheck() {
  this.checkAndClearResolvedConversation();
  this.conversationStatusCheckInterval = setInterval(() => {
    if (this.isWidgetOpen && this.conversationSize > 0) {
      this.checkAndClearResolvedConversation();
    }
  }, 60000);
},
The interval fires every 60 seconds and is gated on two conditions:
  • isWidgetOpen — polling only runs while the chat panel is open. No background polling when the visitor is reading the host page.
  • conversationSize > 0 — polling only runs when a conversation actually exists, preventing console errors on the empty home view.
Additionally, checkAndClearResolvedConversation is called immediately every time the widget bubble is opened (on the toggle-open postMessage event), so a conversation that was resolved while the widget was closed is detected on the very next open. A 404 response from the conversation endpoint is treated the same as a resolved status — the soft exit flow runs in both cases.

Session storage keys

clearSessionStorage removes these keys explicitly, then sweeps any remaining cw_* and cwc* keys:
// custom/widget/store/modules/contacts.js:39-53
const SESSION_KEYS = [
  'cwc-unique-id',
  'cwc-session',
  'cw_contact_uuid',
  'cw_conversation',
  'cw_conversation_id',
  'chatwoot_contact_id',
  'chatwoot_conversation_id',
  'chatwootContactIdentity',
  'user_color',
  'user_uuid',
  'cw_d',
  'cw_auth_token',
  'widget_auth_token',
];
chatwoot_user_data is intentionally excluded from the sweep. It stores the visitor’s name and email so the pre-chat form can be pre-filled the next time they open the widget, without resuming the previous conversation.
Do not add website_token to the list of keys cleared on exit. It is not session data — it identifies which inbox the widget belongs to and is required for every API call. The SDK re-embeds it in the iframe URL on every fresh load.

Build docs developers (and LLMs) love