Acuity Scheduling, now part of Squarespace, powers bookings for salons, clinics, tutors, photographers and consultants. Like every hosted scheduler, the appointment is confirmed inside Acuity's iframe, so your website's Google Tag Manager sees nothing unless you listen for the message Acuity sends when a booking completes.
This guide covers the listener, the GA4 and Google Ads tags, values, the cross-domain cases, and how to test.
The Three Embed Types
- Inline embed. An iframe on your page. Acuity posts a message to the parent window when the appointment is scheduled. Best for tracking.
- Popup or button. Opens Acuity in a new window or lightbox. Depending on the implementation, messages may not reach your page. Treat as cross-domain.
- Direct link. Visitor leaves for acuityscheduling.com or your Acuity subdomain. Cross-domain measurement required.
Check your site and confirm which one you have before setting anything up.
Step 1: Confirm What Acuity Sends
Before writing the listener, open the page with the scheduler, open the browser console and run:
window.addEventListener('message', function (e) { console.log(e.origin, e.data); });
Complete a test booking. You will see Acuity messages for sizing and, on completion, a message that identifies the scheduling confirmation. The exact string has changed between Acuity versions, so confirm it on your site. In my implementations it contains a scheduling complete identifier.
Step 2: The Listener Tag
Create a Custom HTML tag named Listener - Acuity on the pages with the scheduler:
<script>
(function () {
window.addEventListener('message', function (e) {
if (!e.origin || e.origin.indexOf('acuityscheduling.com') === -1) { return; }
var data = e.data;
if (typeof data !== 'string') {
try { data = JSON.stringify(data); } catch (err) { return; }
}
if (data.indexOf('scheduling_complete') === -1 && data.indexOf('acuity:appointment') === -1) { return; }
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
event: 'acuity_booking',
booking_tool: 'acuity',
booking_status: 'complete'
});
});
})();
</script>
Adjust the two match strings to whatever you saw in Step 1. The origin check keeps other iframes from triggering it.
Step 3: Variables and Trigger
Data Layer Variables for booking_status and, if you add them later, appointment_type and booking_value. A Custom Event trigger CE - acuity_booking with the condition booking_status equals complete.
Step 4: GA4 and Google Ads Tags
- GA4 Event
generate_leadwithbooking_toolas a parameter, andvalueandcurrencyif you have them. Mark it as a key event. - Google Ads Conversion Tracking with your conversion ID and label. Set a fixed value per booking in the conversion action, or map
appointment_typeto a value with a Lookup Table variable. Keep the Conversion Linker on All Pages. - Meta Schedule or Lead event if you run Meta ads.
Step 5: Values Per Appointment Type
If your Acuity scheduler is embedded with the appointment type preselected in the URL, read the type from the embed URL with a Custom JavaScript variable and push it with the booking event. Then create a Lookup Table variable mapping each type to its value and use it in the Google Ads tag. This turns one generic booking conversion into value-aware data without changing anything in Acuity.
Step 6: Cross-Domain for Popup and Link Embeds
When the booking happens on Acuity's domain:
- In the Conversion Linker, enable linking across domains and list your domain and the Acuity domain you use.
- Add both domains to the cross-domain list in your Google tag settings so GA4 keeps the session.
- Use Acuity's confirmation page tracking code setting, if available on your plan, to load your GTM container or fire the conversion on the confirmation page.
- Test that the click identifier survives the hand-off by arriving on your site with a test gclid, clicking through to Acuity, and checking the linker parameter on the Acuity URL.
If none of this is possible, track the click into Acuity as a secondary conversion and import confirmed appointments from Acuity's export as offline conversions.
Step 7: Enhanced Conversions
The iframe message does not include the client's email. If you want Enhanced Conversions, add a short email step on your page before the scheduler loads and prefill Acuity with it. Push the email to the dataLayer at that step, create a User-Provided Data variable from it and attach it to the Google Ads conversion tag. The Calendly guide on this site shows the same pattern.
Step 8: Test
- GTM Preview on the scheduler page. Complete a test booking.
- Confirm one
acuity_bookingevent and one fire each of GA4 and Google Ads. - Inspect the Google Ads request in a tag debugger such as the Insighter Debugger extension for the label and value.
- Refresh the page after booking. Nothing should fire again.
- Cancel the test appointment in Acuity.
Common Problems
- No message on completion. Popup embed, or the match string changed. Repeat Step 1.
- Fires on every resize message. The match condition is too loose. Match the completion string only.
- Conversion recorded with no campaign. Cross-domain hand-off lost the click identifier. Check the Conversion Linker settings.
Outcome
Google Ads counts appointments, not scheduler views. Values per appointment type let the account distinguish a consultation from a quick follow-up. And GA4 shows which channels fill the calendar, which is the question every booking-led business is really asking.
