Skip to content

Instantly share code, notes, and snippets.

@prabapro
Last active March 3, 2026 09:40
Show Gist options
  • Select an option

  • Save prabapro/953416353e45045d776dc8f0ca4f06ec to your computer and use it in GitHub Desktop.

Select an option

Save prabapro/953416353e45045d776dc8f0ca4f06ec to your computer and use it in GitHub Desktop.

Text LiveChat (livechat.com) listener for Google Tag Manager

Event name Extra params Notes
livechat_chat_initiated livechat_message_type First customer message
livechat_chat_ended Before survey kicks in
livechat_prechat_submitted Agents online
livechat_offline_submitted Agents offline (same form, different mode)
livechat_survey_submitted Post-chat survey
livechat_rating_submitted livechat_rating_value Thumbs Up / Down / Removed

Instructions

  1. Create a Custom HTML tag with the below code
  2. Fire it on either DOM Ready or Windows Loaded trigger

Docs

Listener

<script>
  /**
 * @file LiveChat Widget — GTM Listener
 * @description Listens to LiveChat Widget JS API callbacks and pushes events
 *              to the dataLayer for Google Tag Manager consumption.
 *              Tracks: chat initiated, chat ended, form submissions (pre-chat,
 *              offline/ticket, post-chat survey), and chat ratings.
 *
 * @see {@link https://platform.text.com/docs/extending-chat-widget/javascript-api} LiveChat Widget JS API
 *
 * @author Praba Ponnambalam (praba@measureschool.com)
 * @date 2026-03-03
 */
(function () {

  // ─── Guard ─────────────────────────────────────────────────────────
  if (typeof LiveChatWidget === 'undefined' || typeof window.dataLayer === 'undefined') {
    console.warn('[LiveChat GTM] LiveChatWidget or dataLayer not found.');
    return;
  }

  var chatInitiated = false;
  var previousCustomerStatus = null;

  // ─── 1. Chat Initiated ─────────────────────────────────────────────
  // Fires on the first message sent by the customer (not a greeting).
  // author.type === 'customer' + no greeting = genuine customer message.
  // chatInitiated flag ensures this fires only once per page load.
  LiveChatWidget.on('new_event', function (data) {
    if (chatInitiated) return;
    if (data.author.type !== 'customer') return;
    if (data.greeting) return;

    chatInitiated = true;

    var payload = {
      event: 'livechat_chat_initiated',
      livechat_message_type: data.type,   // 'message' | 'rich_message' | 'file'
    };
    window.dataLayer.push(payload);
  });

  // ─── 2. Chat Ended ─────────────────────────────────────────────────
  // No dedicated API event — best proxy is the 'chatting' → 'browsing'
  // status transition, which fires when the customer ends the chat
  // (before post-chat survey, if enabled).
  LiveChatWidget.on('customer_status_changed', function (data) {
    if (previousCustomerStatus === 'chatting' && data.status === 'browsing') {
      var payload = { event: 'livechat_chat_ended' };
      window.dataLayer.push(payload);
    }
    previousCustomerStatus = data.status;
  });

  // ─── 3. Form Submitted ─────────────────────────────────────────────
  // Split into three distinct events so each surfaces separately in GA4.
  //
  //   prechat  → livechat_prechat_submitted   (agents online)
  //   ticket   → livechat_offline_submitted   (agents offline, same form)
  //   postchat → livechat_survey_submitted
  var FORM_EVENT_NAMES = {
    prechat:  'livechat_prechat_submitted',
    ticket:   'livechat_offline_submitted',
    postchat: 'livechat_survey_submitted',
  };

  LiveChatWidget.on('form_submitted', function (data) {
    var payload = {
      event: FORM_EVENT_NAMES[data.type] || 'livechat_form_submitted',
    };
    window.dataLayer.push(payload);
  });

  // ─── 4. Rating Submitted ───────────────────────────────────────────
  // Note: value is passed as a bare string, not wrapped in an object.
  // 'none' = customer removed a previously submitted rating.
  var RATING_LABELS = {
    good: 'Thumbs Up',
    bad:  'Thumbs Down',
    none: 'Rating Removed',
  };

  LiveChatWidget.on('rating_submitted', function (value) {
    var payload = {
      event: 'livechat_rating_submitted',
      livechat_rating_value: RATING_LABELS[value] || value,
    };
    window.dataLayer.push(payload);
  });


})();
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment