Skip to content

Instantly share code, notes, and snippets.

@pacotole
Last active April 9, 2026 15:49
Show Gist options
  • Select an option

  • Save pacotole/75bed3c53dcc16550eb4f037d0ec6873 to your computer and use it in GitHub Desktop.

Select an option

Save pacotole/75bed3c53dcc16550eb4f037d0ec6873 to your computer and use it in GitHub Desktop.
Complete event tracking for Joinchat and Joinchat Premium (Chat Funnels, Support Agents, Embeds, Omnichannel)

Joinchat - Custom Events Documentation

This document describes all custom events dispatched by Joinchat Core and Joinchat Premium with its addons. These events can be listened to using document.addEventListener() to extend functionality or integrate with third-party tools.

Table of Contents


Core Events

These events are used internally by Joinchat and can be listened to for tracking or custom integrations.

joinchat:starting

Dispatched before Joinchat initialization begins.

Internal Use: This event is primarily for internal communication between Joinchat Core and Premium addons.

File: joinchat/public/js/joinchat.js

Detail: None

Example:

document.addEventListener('joinchat:starting', () => {
  console.log('Joinchat is starting...');
});

joinchat:start

Dispatched when Joinchat has finished initialization and is ready.

File: joinchat/public/js/joinchat.js

Detail: None

Note: After this event, joinchat_obj.is_ready will be true.

Example:

document.addEventListener('joinchat:start', () => {
  console.log('Joinchat is ready');
  console.log('Settings:', window.joinchat_obj.settings);
});

joinchat:show

Dispatched when the chat dialog is opened.

File: joinchat/public/js/joinchat.js, Multiple (listened in tracking)

Detail:

{
  trigger: string // How the dialog was triggered
}

Trigger values:

  • 'button' - User clicked the main button
  • 'contact' - User clicked contact/open button inside chat
  • 'auto' - Automatically opened after delay
  • 'hover' - Mouse hover trigger (desktop only)
  • 'url' - Opened via URL parameter or anchor (#joinchat)
  • 'trigger' - Triggered by custom element click
  • 'screen' - Triggered by scroll (element visible on screen)
  • 'agent' - Clicked on support agent (Support Agents addon)
  • 'unknown' - Unknown source

Example:

document.addEventListener('joinchat:show', (e) => {
  console.log('Chat opened via:', e.detail.trigger);
});

joinchat:hide

Dispatched when the chat dialog is closed.

File: joinchat/public/js/joinchat.js, Multiple (listened internally)

Detail: None

Example:

document.addEventListener('joinchat:hide', () => {
  console.log('Chat dialog closed');
});

joinchat:bubbles

Dispatched when all chat bubbles have been shown (after animation completes).

Internal Use: This event is primarily for internal communication between Joinchat Core and Premium addons.

File: joinchat/public/js/joinchat.js

Detail: None

Example:

document.addEventListener('joinchat:bubbles', () => {
  console.log('All chat bubbles have been displayed');
});

joinchat:optin

Dispatched when the user toggles the opt-in checkbox.

File: joinchat/public/js/joinchat.js, Multiple (listened in tracking)

Detail:

{
  optin: boolean // true if user accepts, false if rejects
}

Example:

document.addEventListener('joinchat:optin', (e) => {
  console.log('Opt-in status:', e.detail.optin ? 'accepted' : 'rejected');
});

joinchat:event

Dispatched before sending analytics events. This event is cancelable.

File: joinchat/public/js/joinchat.js

Detail:

{
  event_category: string,  // Event category (default: 'JoinChat')
  event_label: string,     // Destination URL or label
  event_action: string,    // Action description (format: "channel: id")
  chat_channel: string,    // Channel name (e.g., 'whatsapp')
  chat_id: string,         // Channel contact (phone, username)
  is_mobile: string,       // 'yes' or 'no'
  page_location: string,   // Current page URL
  page_title: string       // Page title
}

Cancelable: Yes. Return false to prevent analytics events from being sent.

Note: You can modify the params object to customize analytics data before it's sent.

Example:

document.addEventListener('joinchat:event', (e) => {
  console.log('Analytics event:', e.detail);
  
  // Modify params before sending
  e.detail.event_label = 'Custom Label';
  
  // Or cancel the event
  if (someCondition) {
    e.preventDefault();
    return false;
  }
});

joinchat:open

Dispatched when about to open a WhatsApp link or chat channel. This event is cancelable.

File: joinchat/public/js/joinchat.js, addons/omnichannel/public/js/joinchat-omnichannel.js

Detail:

{
  link: string,         // WhatsApp or channel link URL
  chat_channel: string, // Channel app name (e.g., 'whatsapp', 'telegram')
  chat_id: string,      // Channel ID (phone number or username)
  chat_message: string, // Message to send
  trigger: string       // How the action was triggered (see trigger values above)
}

Cancelable: Yes. Return false to prevent the link from opening.

Note: You can modify the params object to customize the link or message before opening.

Example:

document.addEventListener('joinchat:open', (e) => {
  console.log('Opening channel:', e.detail.chat_channel);
  console.log('Message:', e.detail.chat_message);
  console.log('Triggered by:', e.detail.trigger);
  
  // Modify params before opening
  e.detail.chat_message = 'Custom message';
  
  // Or cancel if certain condition
  if (someCondition) {
    e.preventDefault();
    return false;
  }
});

Chat Funnels Events

Events dispatched by the Chat Funnels addon.

joinchat:funnel

Dispatched to load a specific chat funnel.

Internal Use: This event is primarily for internal communication between Joinchat Core and Premium addons.

File: addons/chat-funnels/public/js/joinchat-chat-funnels.js (listened)

Detail:

{
  funnel: number,  // Funnel ID to load
  start_at: number, // Step number to start at (default: 0)
  reset: boolean    // Whether to reset chat log and inputs (default: false)
}

Example:

// Trigger loading a funnel
document.dispatchEvent(new CustomEvent('joinchat:funnel', {
  detail: { funnel: 1, start_at: 0, reset: true }
}));

joinchat:bubble

Dispatched when a new chat bubble is added to the conversation.

File: addons/chat-funnels/public/js/joinchat-chat-funnels.js

Detail:

{
  bubble: HTMLElement, // The bubble DOM element
  message: string      // The message content (HTML)
}

Example:

document.addEventListener('joinchat:bubble', (e) => {
  console.log('New message:', e.detail.message);
  console.log('Bubble element:', e.detail.bubble);
});

joinchat:goto

Dispatched when a user selects a "goto" option in a chat funnel.

File: addons/chat-funnels/public/js/joinchat-chat-funnels.js

Detail:

{
  text: string, // Option text selected by user ([direct] if no text)
  step: string  // Step identifier in format "funnel_id-step_number"
}

Example:

document.addEventListener('joinchat:goto', (e) => {
  console.log('User selected:', e.detail.text);
  console.log('Going to step:', e.detail.step);
});

joinchat:input

Dispatched when a user submits input in a chat funnel.

File: addons/chat-funnels/public/js/joinchat-chat-funnels.js

Detail:

{
  name: string,  // Input field name
  value: string, // Input value entered by user
  input: string, // Input type ('text', 'email', 'phone', 'number', 'date', 'time', 'select')
  text: string   // Display text (only for 'select' type)
}

Example:

document.addEventListener('joinchat:input', (e) => {
  console.log(`Field "${e.detail.name}" (${e.detail.input}):`, e.detail.value);
});

joinchat:link

Dispatched when a user clicks a link option in a chat funnel.

File: addons/chat-funnels/public/js/joinchat-chat-funnels.js

Detail:

{
  link: string,     // The link URL
  type: string,     // Link type: 'anchor', 'external', or 'internal'
  text: string      // Link display text
}

Example:

document.addEventListener('joinchat:link', (e) => {
  console.log('Link clicked:', e.detail.link);
  console.log('Link type:', e.detail.type);
});

joinchat:contact

Dispatched when contact options are enabled in the chat.

File: addons/chat-funnels/public/js/joinchat-chat-funnels.js

Detail:

{
  mode: string // Contact mode: 'default', 'custom', or 'custom_message'
}

Example:

document.addEventListener('joinchat:contact', (e) => {
  console.log('Contact enabled with mode:', e.detail.mode);
});

joinchat:ai

Dispatched when control is passed to an AI agent.

File: addons/chat-funnels/public/js/joinchat-chat-funnels.js

Detail:

{
  chat: Array,    // Chat log with role and content
  inputs: Object, // User inputs collected so far
  metas: Object   // Metadata (url, title, date, time, lang, etc.)
}

Example:

document.addEventListener('joinchat:ai', (e) => {
  console.log('AI agent activated');
  console.log('Chat history:', e.detail.chat);
  console.log('User inputs:', e.detail.inputs);
  console.log('Metadata:', e.detail.metas);
});

joinchat:submitSuccess

Dispatched when data submission to the server succeeds.

File: addons/chat-funnels/public/js/joinchat-chat-funnels.js

Detail:

{
  response: any,   // Server response (parsed JSON or text)
  data: Object     // Submission data sent to server
}

Example:

document.addEventListener('joinchat:submitSuccess', (e) => {
  console.log('Data submitted successfully');
  console.log('Server response:', e.detail.response);
});

joinchat:submitError

Dispatched when data submission to the server fails.

File: addons/chat-funnels/public/js/joinchat-chat-funnels.js

Detail:

{
  error: Error,  // Error object with message
  data: Object   // Submission data that failed
}

Example:

document.addEventListener('joinchat:submitError', (e) => {
  console.error('Submission failed:', e.detail.error.message);
  console.log('Failed data:', e.detail.data);
});

joinchat:webhookSuccess

Dispatched when a webhook call succeeds.

File: addons/chat-funnels/public/js/joinchat-chat-funnels.js

Detail:

{
  response: any,   // Webhook response (parsed JSON or text)
  payload: Object  // Payload sent to webhook
}

Example:

document.addEventListener('joinchat:webhookSuccess', (e) => {
  console.log('Webhook called successfully');
  console.log('Response:', e.detail.response);
});

joinchat:webhookError

Dispatched when a webhook call fails.

File: addons/chat-funnels/public/js/joinchat-chat-funnels.js

Detail:

{
  error: Error,    // Error object with message
  payload: Object  // Payload that failed to send
}

Example:

document.addEventListener('joinchat:webhookError', (e) => {
  console.error('Webhook failed:', e.detail.error.message);
  console.log('Failed payload:', e.detail.payload);
});

joinchat:qr

Internal event to regenerate QR codes with updated parameters.

Internal Use: This event is primarily for internal communication between Joinchat Core and Premium addons.

File: addons/chat-funnels/public/js/joinchat-chat-funnels.js

Detail:

{
  id: string,      // Phone ID (empty for rebuild with same phone)
  message: string  // Custom message for QR
}

Support Agents Events

Events dispatched by the Support Agents addon.

joinchat:agents

Dispatched when support agents are initialized or status changes.

Internal Use: This event is primarily for internal communication between Joinchat Core and Premium addons.

File: addons/support-agents/public/js/joinchat-support-agents.js

Detail:

{
  agents: Array,  // Array of agent objects with name, phone, role, online status
  status: string  // Overall status: 'online', 'offline', or 'none'
}

Example:

document.addEventListener('joinchat:agents', (e) => {
  console.log('Agents status:', e.detail.status);
  console.log('Agents:', e.detail.agents);
  
  e.detail.agents.forEach(agent => {
    console.log(`${agent.name}: ${agent.online ? 'online' : 'offline'}`);
  });
});

joinchat:expand_agents

Dispatched when the collapsed agents list is expanded.

File: addons/support-agents/public/js/joinchat-support-agents.js

Detail:

{
  agents_count: number,  // Total number of agents
  agents_online: number  // Number of online agents
}

Example:

document.addEventListener('joinchat:expand_agents', (e) => {
  console.log(`Showing ${e.detail.agents_count} agents`);
  console.log(`${e.detail.agents_online} are online`);
});

Omnichannel Events

No additional events beyond joinchat:open documented in Core Events.


Media Events

Events related to embedded media like videos.

joinchat:video

Dispatched when a video is played or paused in the chat.

File: public/js/joinchat-premium-embeds.js

Detail:

{
  action: string,  // Video action: 'play' or 'pause'
  service: string, // Video service: 'vimeo' or 'youtube'
  id: string       // Video ID
}

Example:

document.addEventListener('joinchat:video', (e) => {
  console.log(`Video ${e.detail.action}:`, e.detail.id);
  console.log('Service:', e.detail.service);
});

Event Usage Patterns

Listening to Multiple Events

// Track all funnel interactions
['joinchat:goto', 'joinchat:input', 'joinchat:link'].forEach(eventName => {
  document.addEventListener(eventName, (e) => {
    console.log(`Event: ${eventName}`, e.detail);
    // Send to analytics
  });
});

Triggering Events

// Load a specific funnel
document.dispatchEvent(new CustomEvent('joinchat:funnel', {
  detail: {
    funnel: 2,
    start_at: 5,
    reset: false
  }
}));

// Regenerate QR codes with custom message
document.dispatchEvent(new CustomEvent('joinchat:qr', {
  detail: {
    id: '',
    message: 'Custom WhatsApp message'
  }
}));

Canceling Events

joinchat:open and joinchat:event are cancelable:

// Cancel opening WhatsApp
document.addEventListener('joinchat:open', (e) => {
  if (!userHasPermission) {
    e.preventDefault();
    return false;
  }
});

// Cancel analytics events
document.addEventListener('joinchat:event', (e) => {
  if (privacyMode) {
    e.preventDefault();
    return false;
  }
});

Notes

  • All events are dispatched on the document object
  • Event details can be accessed via event.detail
  • Most events are for listening/tracking purposes
  • Internal events (primarily for Core-Premium communication): joinchat:starting, joinchat:bubbles, joinchat:funnel, joinchat:agents, joinchat:qr
  • Events that can be triggered externally (though primarily internal): joinchat:funnel, joinchat:qr
  • Cancelable events: joinchat:open, joinchat:event (return false or call preventDefault())
  • joinchat:start indicates Joinchat is fully initialized and ready (joinchat_obj.is_ready === true)
  • Trigger values for joinchat:show and joinchat:open: 'button', 'contact', 'auto', 'hover', 'url', 'trigger', 'screen', 'agent', 'unknown'
/**
* Joinchat - Advanced Analytics Tracking
* Complete event tracking for Joinchat and Joinchat Premium
* (Chat Funnels, Support Agents, Embeds, Omnichannel)
*/
((window, document, joinchat_obj) => {
'use strict';
/**
* Send event to Google Analytics and Google Tag Manager
* @param {string} action - Event action
* @param {string} [label=''] - Event label
* @param {number} [value=null] - Event value
*/
const trackEvent = (action, label = '', value = null) => {
const category = joinchat_obj.settings.event_category || 'Joinchat';
const eventData = {
event_category: category,
event_label: label,
};
if (value !== null) eventData.value = value;
// Google Analytics 4 (gtag.js)
if (typeof gtag === 'function') {
gtag('event', action, eventData);
}
// Google Tag Manager (dataLayer)
const data_layer = window[joinchat_obj.settings.data_layer] || window[window.gtm4wp_datalayer_name] || window.dataLayer;
if (typeof data_layer === 'object') {
data_layer.push({
'event': 'joinchat_event',
'event_category': category,
'event_action': action,
'event_label': label,
'event_value': value,
'is_mobile': joinchat_obj.is_mobile ? 'yes' : 'no',
'page_location': location.href,
'page_title': document.title || 'no title'
});
}
};
// Chat window shown
document.addEventListener('joinchat:show', (e) => {
const trigger = e.detail?.trigger || 'unknown';
trackEvent('chat_open', trigger);
});
// Chat window closed
document.addEventListener('click', (e) => {
if (e.target.closest('.joinchat__close')) {
trackEvent('chat_close');
}
});
// Opt-in toggled
document.addEventListener('joinchat:optin', (e) => {
const status = e.detail.optin ? 'accept' : 'reject';
trackEvent('optin_toggle', status);
});
// Chat Funnel / User selects an option
document.addEventListener('joinchat:goto', (e) => {
const stepText = e.detail.text || '[none]';
const stepNumber = e.detail.step || 0;
trackEvent('goto', stepText, stepNumber);
});
// Chat Funnel / User fills input field
document.addEventListener('joinchat:input', (e) => {
const inputType = e.detail.input || 'text';
const fieldName = e.detail.name || 'unknown';
trackEvent('input', `${fieldName} (${inputType})`);
});
// Chat Funnel / Control passed to AI
document.addEventListener('joinchat:ai', () => {
trackEvent('ai_agent');
});
// Chat Funnel / Contact options shown
document.addEventListener('joinchat:contact', (e) => {
const mode = e.detail?.mode || 'default';
trackEvent('contact_show', mode);
});
// Chat Funnel / Data submission success
document.addEventListener('joinchat:submitSuccess', () => {
trackEvent('submit_success');
});
// Chat Funnel / Data submission error
document.addEventListener('joinchat:submitError', (e) => {
const error = e.detail.error?.message || 'Unknown error';
trackEvent('submit_error', error);
});
// Chat Funnel / Webhook success
document.addEventListener('joinchat:webhookSuccess', () => {
trackEvent('webhook_success');
});
// Chat Funnel / Webhook error
document.addEventListener('joinchat:webhookError', (e) => {
const error = e.detail.error?.message || 'Unknown error';
trackEvent('webhook_error', error);
});
// Chat Funnel / Chat funnel loaded
document.addEventListener('joinchat:funnel', (e) => {
const funnelId = e.detail.funnel || 0;
trackEvent('funnel_load', `Funnel ${funnelId}`);
});
// Chat Funnel / Link option clicked
document.addEventListener('joinchat:link', (e) => {
const linkType = e.detail.type || 'unknown';
const link = e.detail.link?.substring(0, 100) || '';
trackEvent('option_link', `${linkType}: ${link}`);
});
// Video play/pause
document.addEventListener('joinchat:video', (e) => {
const action = e.detail.action || 'unknown';
const service = e.detail.service || 'unknown';
const videoId = e.detail.id || '';
trackEvent(`video_${action}`, `${service}: ${videoId}`);
});
// Support Agents / Expand agents list
document.addEventListener('joinchat:expand_agents', (e) => {
const agentsCount = e.detail.agents_count || 0;
const agentsOnline = e.detail.agents_online || 0;
trackEvent('agents_expand', `${agentsOnline}/${agentsCount} online`);
});
})(window, document, window.joinchat_obj || {});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment