Calendly Event Listener: Track Booked Meetings in GTM
A booked call is usually your most valuable conversion. This listener reports every stage of the Calendly widget, not just the booking.

A booked meeting is usually the most valuable thing that happens on a B2B website. It is also one of the least tracked, because Calendly runs in an iframe and most people never get past that.
This listener reports every stage of the Calendly widget, not just the booking.
What the listener reports
Calendly posts messages to the parent window as the visitor moves through the widget. The listener catches any message whose event name starts with calendly and pushes a single data layer event with the stage attached.
The stages you will see include the profile page loading, the date and time being selected, and the event being scheduled. That last one is the booking.
The listener code
This is the Calendly listener from DumbData.
The full listener is in the copy block at the bottom of this page, with a copy button. It is reproduced exactly as DumbData publish it, so you can copy it straight from here or from their page.
Notice the design. Rather than one data layer event per Calendly stage, it pushes one event called calendly with a calendly_event value describing the stage. That keeps your GTM setup small.
Installing it in GTM
Create a Custom HTML tag and paste the listener.
Trigger on the pages with the Calendly embed.
Save and Preview.
Creating the trigger and variable
Data Layer Variable named calendly_event. Call it DLV Calendly Stage.
Custom Event trigger, event name calendly, set to Some Custom Events where DLV Calendly Stage equals event_scheduled. Save it as CE Calendly Booked.
Optionally a second trigger with no condition, for funnel reporting on every stage.
The condition is the important part. Without it, your conversion fires when someone merely opens the widget.
Sending it to GA4
Create a GA4 Event tag with the event name schedule_meeting or generate_lead.
Add a parameter for the stage from your Data Layer Variable.
Trigger: CE Calendly Booked.
Send the other stages as a separate, non conversion event so you can see how many people open the widget and never book. That drop off is often large and usually fixable.
Sending conversions to Google Ads
Create a conversion action in Google Ads.
Add the conversion tag in GTM, triggered on CE Calendly Booked.
Give it a real value based on your close rate from booked calls: setting conversion values
A booked call is worth far more than a form fill, and bidding should know that.
What happens after the booking
A booked call is a step, not a sale. Plenty of them never show up.
Once this is working, the next step is sending the actual outcome back from your CRM, so bidding learns which campaigns produce meetings that turn into customers: tracking booked calls from Calendly to Google Ads.
Testing
Open GTM Preview on the page with the embed.
Open the widget and confirm a calendly event fires with a stage value.
Pick a date and time and confirm another fires.
Complete a real test booking.
Confirm event_scheduled appears and your conversion tag fired once.
Cancel the test booking afterwards so you do not confuse your own calendar.
Common issues
Nothing fires. You are linking out to Calendly rather than embedding it. Only the embed can postMessage back.
Conversion fires on widget open. The trigger condition on event_scheduled is missing.
Stage value is undefined. The variable name must be exactly calendly_event.
Privacy considerations
Never send names, emails, phone numbers or free text into GA4.
Hash anything you send to an ad platform for matching.
Gate the tags behind marketing consent: Consent Mode v2 in GTM
Final thoughts
If you sell by conversation, this is probably the highest value listener on the whole DumbData library for you. Fire the conversion on event_scheduled, value it properly, and then close the loop from your CRM.
Listener from DumbData.
Need this built properly?
A booked call deserves a conversion value that reflects what it is worth. I set up and repair tracking for ecommerce stores and lead generation businesses, so the numbers in your ad accounts match the money in your bank account.
Have a look at what I do, or book a call and we can go through your setup together.
<script>
window.dataLayer = window.dataLayer ||[];
window.addEventListener('message',
function(e) {
if (e.data.event && e.data.event.indexOf('calendly') === 0) {
window.dataLayer.push({
'event' : 'calendly',
'calendly_event' : e.data.event.split('.')[1]
});
}
}
);
</script>


















