Analytics & Tracking · Northeast Florida

Track Calendly Bookings Embedded on Your Website | Capture Emails

An embedded Calendly widget lives in an iframe, so ordinary click tracking cannot see inside it. Here is how to catch the booking, and how to get the email without handing your API token to the entire internet.

Google Tag Manager configured to track Calendly booking events

Why the usual triggers do not work

An embedded Calendly widget runs in an iframe. Your Google Tag Manager container sits on the parent page, and browsers deliberately keep the two apart. Click triggers see nothing.

Calendly gets around this by broadcasting a postMessage event to the parent page when something happens. Somebody picks a date, fills out the form, confirms a booking. Each one fires a message you can listen for.

So the job is: listen for that message, push it into the data layer, and fire your tags off it.

Step 1: add the Calendly listener

In GTM, create a new tag, choose Custom HTML, and paste this in. Set the trigger to All Pages.

<script>
window.dataLayer = window.dataLayer || [];
window.addEventListener('message', function(e) {
    if (e.data.event && e.data.event.indexOf('calendly') === 0) {
        if (e.data.event == 'calendly.event_scheduled') {
            var originalString = e.data.payload.invitee.uri;
            var regex_event = /scheduled_events\/(.*?)\/invitees/;
            var event_uuid = originalString.match(regex_event);
            var regex_invitee = /invitees\/(.*)/;
            var invitee_uuid = originalString.match(regex_invitee);

            window.dataLayer.push({
                'event': 'calendly',
                'calendly_event': e.data.event.split('.')[1],
                'event_uuid': event_uuid[1],
                'invitee_uuid': invitee_uuid[1]
            });
        } else {
            window.dataLayer.push({
                'event': 'calendly',
                'calendly_event': e.data.event.split('.')[1]
            });
        }
    }
});
</script>

This listens for every Calendly message and pulls the event and invitee IDs out of the URI on a confirmed booking.

Step 2: create the data layer variables

Under Variables, User-Defined Variables, click New and add three Data Layer Variables. Name them to match what the listener pushes:

  • calendly_event, which tells you which stage fired
  • event_uuid
  • invitee_uuid

The two UUIDs identify the specific booking. You need them for the next part.

Step 3: create the booking trigger

Triggers, New, Custom Event. Set the event name to calendly, and add a condition so it only fires when your calendly_event variable equals event_scheduled.

Without that condition it fires on every stage of the widget, including people who open it and wander off. You would be counting browsers as bookings.

Point a GA4 event tag at this trigger and you are done with the tracking half. You now know how many bookings came through, and which page they came from.

Keep the token off the page

Calendly's docs say a personal access token should be kept private, "just like a password." Anything you put in a web GTM container is public. Those two facts do not coexist.

Getting the email, safely

Here is where this guide used to give bad advice, and where most guides still do.

The booking message does not include the invitee's email. To get it you have to call Calendly's API, and that call needs an API token. The tempting move is to drop the token into a Custom JavaScript Variable in GTM and call the API from the browser.

Do not do that.

A web GTM container is a public file. Anyone can fetch it from googletagmanager.com using the container ID that sits in your page source. A token pasted in there is readable by anyone who looks, and a Calendly personal access token can read your account's scheduled events and invitee details. Names. Emails. Your customers' information, not just yours.

Calendly is explicit about this. Their documentation says a personal access token should be kept private and secure, just like a password, and used only with internal applications.

Two ways to do it properly, both keeping the token on a server where it belongs:

Calendly webhooks. The cleaner option. Subscribe to the invitee.created webhook and Calendly posts the full booking to your endpoint the moment it happens, email included. No token in the browser, and it still works if somebody blocks your tags.

Server-side GTM. If you already run a server container, store the token there as a constant and make the API call from the server. More setup, and it fits neatly if the rest of your tagging already lives there.

Either way, the email reaches Google Ads the same way through enhanced conversions. It just never passes through a visitor's browser to get there.

One more note if you go looking at older examples. Most of them call the API with a synchronous XMLHttpRequest, the version with false as the third argument. That blocks the browser's main thread while it waits, which browsers have been warning about for years. Another good reason to keep this work off the page.

Common questions

Why can't GTM see my Calendly bookings?

The embedded widget runs in an iframe, and browsers deliberately keep it apart from the parent page where your container sits, so click triggers see nothing. Calendly gets around this by broadcasting a postMessage event you can listen for, push into the data layer, and fire your tags from.

How do I count only real bookings?

Add a condition so your trigger only fires when the calendly_event variable equals event_scheduled. Without that condition it fires on every stage of the widget, including people who open it and wander off, and you would be counting browsers as bookings instead of actual appointments.

Can I put my Calendly API token in GTM to grab emails?

No. A web GTM container is a public file anyone can fetch, and a personal access token can read your account's scheduled events and invitee details, meaning your customers' information, not just yours. Calendly says to keep it private like a password, and a web container isn't private.

How do I get the invitee's email safely?

Keep the token on a server where it belongs. The cleaner option is subscribing to Calendly's invitee.created webhook, which posts the full booking to your endpoint the moment it happens, email included. If you already run a server-side GTM container, store the token there and call the API from it.

Does your homepage pass the three second test?

A visitor decides in about three seconds whether your site is for them. Put your address in and I will send you what a stranger actually takes from it, and the specific things to fix.

Scan my site free →

Where to start

Need help tracking your small business marketing events? gravityGone sets up tracking, websites, and marketing automation for small businesses across Jacksonville and Northeast Florida.

Mike Finocchiaro

Mike Finocchiaro

Mike is the founder of gravityGone, where he helps small businesses in Northeast Florida grow through Web Development, SEO, and Marketing Automation.

Learn more about Mike →

Go to Top