FreeGoogle Tag Manager recipes

Track Wix Bookings in Google Tag Manager with this Free GTM Recipe

A free Google Tag Manager recipe that captures Wix bookings and gives you a Trigger you can use to fire conversions to Google Ads, Google Analytics and more.

Aaron Beashel
Aaron BeashelUpdated July 20267 min read
The Wix Scheduling recipe tag inside Google Tag Manager

Google Tag Manager is a common way to send conversions to Google Ads, Google Analytics, etc when someone books an appointment through Wix Bookings on your website.

The problem is, it's quite difficult to set up. Wix Bookings gives no signal when a booking completes, and the confirmation happens on a completely different page, so Google Tag Manager's built-in triggers have nothing to hook into and you're left having to write custom code to detect the booking, send an event to the dataLayer, and build a trigger on top of that.

And unless you're a developer or very experienced with Google Tag Manager, you're going to struggle.

Fortunately though, there is a simple solution.

You can simply install this Wix Scheduling Conversion Tracking Recipe in your Google Tag Manager account and you'll have everything you need to get basic conversion tracking set up.

What Is a Google Tag Manager Recipe and What's in It?

A recipe is a pre-built bundle of Google Tag Manager assets (a tag, a trigger and two variables) that you import in one go instead of building each piece yourself.

This recipe detects appointments booked through Wix Bookings, sends an 'Appointment Booked' event to Google Tag Manager, and gives you a Trigger you can use to fire conversions to Google Analytics, Google Ads, etc.

Here's exactly what each piece contains, so you can see what you're importing before you import it:

Custom HTML Tag

Detects successful Wix Bookings appointments and pushes a 'wix_appointment_scheduled' event to the dataLayer. It fires on All Pages, once per event, and this is the code inside it:

The Tag's CodeJavaScript · 171 lines
(function () {
  if (window.__converlyRecipeWixScheduling) return;
  window.__converlyRecipeWixScheduling = true;
  window.dataLayer = window.dataLayer || [];

  // The booking CTA's Wix-controlled data-hook (app-level, theme-stable).
  var BOOK_NOW_HOOK = 'booking-details-book-now-cta';
  // The order-confirmation page's Wix-controlled marker (slug-independent).
  var THANKYOU_HOOK = 'ThankYouPageAppDataHook.root';

  // localStorage key for the cross-page marker. Deliberately distinct from
  // the production loader's key so this recipe never collides with it.
  var STASH_KEY = '__cvlyRecipeWixSchedStash';
  // How long a click-marker stays valid. The form→confirmation navigation
  // is immediate for free services; generous to absorb slow networks and
  // (best-effort) a paid service's checkout step.
  var STASH_TTL_MS = 10 * 60 * 1000; // 10 minutes
  // How long, on a given page load, we wait for the confirmation marker to
  // render (Wix boots its widgets asynchronously) before giving up.
  var THANKYOU_WAIT_MS = 12000;

  function now() {
    return Date.now();
  }

  function writeStash(obj) {
    try {
      window.localStorage.setItem(STASH_KEY, JSON.stringify(obj));
    } catch (e) {
      // Private mode / storage disabled — can't bridge the navigation, so
      // the booking simply won't be tracked. Fail quietly.
    }
  }

  function readStash() {
    try {
      var raw = window.localStorage.getItem(STASH_KEY);
      if (!raw) return null;
      var obj = JSON.parse(raw);
      return (obj && typeof obj === 'object') ? obj : null;
    } catch (e) {
      return null;
    }
  }

  function clearStash() {
    try { window.localStorage.removeItem(STASH_KEY); } catch (e) {}
  }

  function isFreshStash(stash) {
    return !!(stash && typeof stash.ts === 'number' && (now() - stash.ts) <= STASH_TTL_MS);
  }

  function fireConversion(bookingId, pageUrl) {
    var payload = {
      event: 'wix_appointment_scheduled',
      booking_page_url: pageUrl || ''
    };
    if (bookingId) payload.booking_id = bookingId;
    window.dataLayer.push(payload);
  }

  // ---- Booking form page: mark the Book Now click ----
  //
  // Simplification vs. the production module: because this recipe never
  // reads or reports form field values, it doesn't need to locate the
  // specific booking-form widget on the page (the source module climbs the
  // DOM to scope a field walk to the correct form) — matching on the
  // Wix-controlled CTA hook alone is enough to know a booking attempt was
  // made.
  function handleClick(event) {
    try {
      var target = event.target;
      if (!target || !target.closest) return;

      var btn = target.closest('[data-hook="' + BOOK_NOW_HOOK + '"]');
      if (!btn) return;

      writeStash({
        // The page where the booking was submitted — the meaningful
        // pageUrl for the conversion (the confirmation page's URL carries
        // only a random booking UUID).
        pageUrl: window.location.pathname,
        ts: now()
      });
    } catch (e) {
      // Never throw back into Wix's own click handling.
    }
  }

  // ---- Confirmation page: fire on the success marker ----

  function isThankYouPage() {
    try {
      return !!document.querySelector('[data-hook="' + THANKYOU_HOOK + '"]');
    } catch (e) {
      return false;
    }
  }

  function extractBookingId() {
    try {
      var m = window.location.pathname.match(
        /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/i
      );
      return m ? m[0] : null;
    } catch (e) {
      return null;
    }
  }

  function fireFromThankYou() {
    // Consume-first idempotency: the stash is the single source of truth.
    // Clearing it before firing means a second call (observer + immediate
    // check racing) is a no-op.
    var stash = readStash();
    if (!stash) return;
    if (!isFreshStash(stash)) { clearStash(); return; }

    clearStash(); // consume before firing so re-entry can't double-fire

    fireConversion(extractBookingId(), stash.pageUrl);
  }

  // Runs at load on EVERY page. If a fresh booking marker exists and this
  // page is the confirmation page, fire. If the page hasn't rendered the
  // marker yet (Wix boots widgets async), watch for it within a bounded
  // window. On non-confirmation pages the watcher simply times out and
  // disconnects — harmless.
  function maybeFireOnThankYou() {
    var stash = readStash();
    if (!stash) return;
    if (!isFreshStash(stash)) { clearStash(); return; }

    if (isThankYouPage()) { fireFromThankYou(); return; }

    var done = false;
    var observer = null;
    var timer = null;

    function cleanup() {
      done = true;
      if (observer) { try { observer.disconnect(); } catch (e) {} observer = null; }
      if (timer) { clearTimeout(timer); timer = null; }
    }

    function check() {
      if (done) return;
      if (isThankYouPage()) {
        cleanup();
        fireFromThankYou();
      }
    }

    try {
      observer = new MutationObserver(check);
      observer.observe(document.documentElement || document.body, {
        childList: true,
        subtree: true
      });
    } catch (e) {
      // MutationObserver unavailable — the immediate check above already ran.
    }
    timer = setTimeout(cleanup, THANKYOU_WAIT_MS);
  }

  if (typeof document !== 'undefined' && document.addEventListener) {
    document.addEventListener('click', handleClick, true); // booking form
    maybeFireOnThankYou();                                  // confirmation page
  }
})();

Variables

Captures the booking ID and the booking page URL, which you can use to filter conversions based on certain conditions. These are their exact settings:

Name

DLV - Wix Booking ID

Variable Type

Data Layer Variable

Data Layer Variable Name

booking_id

Name

DLV - Wix Booking Page URL

Variable Type

Data Layer Variable

Data Layer Variable Name

booking_page_url

Trigger

Fires when the 'wix_appointment_scheduled' event is received. Use it as the trigger for any of your conversion tags. These are its exact settings:

Name

CE - Wix Appointment Scheduled

Trigger Type

Custom Event

Event Name

wix_appointment_scheduled

Download This Recipe

Get the ready-to-import file and upload it into Google Tag Manager. Everything shown above (the tag, the trigger and both variables) comes pre-built inside.

Built from Converly's own Wix Bookings detection code

Why It's the Best Recipe Out There

It is built from the same detection code we use in our own conversion tracking product, Converly, just adapted to run inside Google Tag Manager.

Only fires on a successful booking. A visitor who starts but abandons the booking is never counted, because the recipe checks for Wix's own confirmation marker before firing.
Built from the same Wix Bookings detection logic we use in Converly and tested using the same production-grade testing infrastructure we use
Has been used to detect thousands of Wix bookings by our customers.

How to Use the Wix Scheduling GTM Recipe

1

Install the Recipe

Download the recipe using the form above, then import it into your Google Tag Manager container. In GTM, head to Admin, choose Import Container, select the downloaded file, and pick the Merge option (so it doesn't overwrite anything already in your container). The listener, variables and trigger are added for you automatically.

Importing the recipe container in Google Tag Manager
2

Configure Your Tags

Now that the recipe is installed, you can use it as a Trigger for your conversion tags. Here are two common examples.

Example 1: Google Analytics

Create a GA4 event tag and set it to fire on the Trigger the recipe gives you (called CE - Wix Appointment Scheduled). Every Wix booking now sends an event to Google Analytics.

A GA4 event tag using the recipe's trigger

Example 2: Google Ads

Add your Google Ads conversion tag and set it to fire on the recipe's trigger (called CE - Wix Appointment Scheduled). New Wix bookings will now be sent as conversions to Google Ads.

A Google Ads conversion tag using the recipe's trigger

What This Enables You to Do (and What It Doesn't)

This recipe fires an event in Google Tag Manager whenever a Wix Bookings appointment is detected. You can use that as a trigger to send conversions to Google Ads, Google Analytics and other tags.

What it does

Sends an Event to GTM

Detects successful appointments made through Wix Bookings and pushes an event called 'wix_appointment_scheduled' into your dataLayer automatically.

Tells You Which Booking It Was

Captures the booking ID and the booking page URL, so you can tell bookings apart and fire different tags for different services.

Creates a Trigger in GTM

Gives you a 'Trigger' in Google Tag Manager that you can use to fire any of your conversion tags.

What it doesn't do

Doesn't send the conversion server-side

Conversions are sent to Google, Meta, etc through the visitor's browser, so ad blockers and privacy settings in browsers can block them before they arrive.

No Click IDs or Identifiers

It does not capture the Google click ID, Facebook click ID, Meta pixel cookies, IP address or user agent, which means ad platforms will have a hard time matching the conversions back to a campaign (and it won't show in your reports if they can't).

No Personal Details

The script only fires an event. It does not capture the lead's name, email or phone number, all of which Google and Meta require for Enhanced Conversions (Google) and a high Event Match Quality score (Meta).

No Logging or Notifications

You can test it with Tag Assistant when you first set it up, but unless you check every week you have no way of knowing if it breaks (you'll just start to see 0 conversions in your accounts all of a sudden).

The Proper Way to Send Conversions to Google Ads, Meta Ads & More

This recipe is fine if you only send events to Google Analytics, which does not accept personal information or IP addresses. But to get a good Event Match Quality (EMQ) score in Meta, or use Enhanced Conversions in Google, you need to send the data server-side, together with the lead's name and email. That is what Converly does.

A Converly workflow sending Wix Scheduling conversions to ad platforms

Here's why Converly is the best way to send conversions to Google Ads, Meta Ads, Microsoft Ads, LinkedIn Ads, etc.

Easy to set up

Converly gives you a simple drag-and-drop builder you can use to build a Conversion Flow. Simply select a trigger (like an appointment being booked through Wix Bookings) and then select your actions (like sending a conversion to Google Ads). No custom code, no complex configuration.

Sends Data Server-Side

Instead of sending conversions through the visitor's browser, Converly sends them directly from its servers to Google, Meta, etc., so ad blockers and privacy restrictions can't get in the way.

Sends More Data

It captures the lead's name, email and phone plus the Google and Facebook click IDs, IP address, user agent, etc. and sends them with every conversion. That enables Enhanced Conversions in Google Ads, or gives you an Event Match Quality (EMQ) score of 9 or 10 in Meta.

Full Conversion Log & Email Notifications

See every conversion, what was captured, what was sent to each platform and whether it was received. Get email alerts so tracking can't break without you knowing.

Supports Multiple Tools & Destinations

Detects submissions across 100+ form, scheduling and chat tools and sends conversions to 20+ destinations including Google Ads, Meta, ChatGPT, LinkedIn, Microsoft and TikTok. So if you ever add a new tool or start advertising on a new platform, it's just a few clicks to set up proper conversion tracking.

Provides Free Support

Our team has 20 years of experience in conversion tracking and analytics and is here to help whenever you need it. With Google Tag Manager, you're on your own.

Start Your Free Trial

Start a 14-day free trial and track your conversions the proper way. No code, no complicated configuration.

About the author

Aaron Beashel
Aaron Beashel

Aaron is the founder of Converly. With over 15 years of experience in digital marketing and SaaS, he's passionate about helping businesses track and optimise their ad conversions.

Start your free trial

Easily send conversions to your ad platforms and analytics tools.
No code required.