Skip to main content

Calendly GTM Event Listener

Track Calendly booking widget interactions in GTM. Monitor profile page views, event type views, date/time selections, and meeting scheduled events. Fire GA4 and Google Ads conversions on confirmed bookings.

calendlyschedulingbookingmeetingsgtmga4b2blead-generationconversions

Event fired

calendly

Key variable

calendly_event

Calendly

Overview

Calendly is the most popular scheduling tool for sales, support, and consulting teams. Its embedded widget communicates via window.postMessage. This listener captures all Calendly scheduling events, from page views to confirmed bookings, enabling accurate conversion tracking.

Event fired: calendly (all events use one event name) Variable: calendly_event, describes the specific interaction type

Why Use This Listener

A Calendly booking is one of the highest-intent conversion events in a B2B funnel. Without this listener, you cannot:

  • Track which marketing channels drive demo bookings
  • Fire Google Ads conversion tags on scheduled meetings
  • Build Google/Meta audiences from people who booked meetings
  • Calculate demo-request-to-booking rates in GA4

Calendly Event Types

calendly_eventDescription
calendly.profile_page_viewedBooking page loads
calendly.event_type_viewedUser views specific event type
calendly.date_and_time_selectedUser picks a time slot
calendly.event_scheduledBooking confirmed ← primary conversion

How It Works

Calendly sends postMessage events from its embedded iframe as users interact with the booking widget. The listener uses window.addEventListener('message', ...) to intercept these.

mermaid
sequenceDiagram
    participant V as Visitor
    participant C as Calendly Widget
    participant L as Listener
    participant DL as dataLayer
    participant GTM as GTM

    V->>C: Views booking page
    C->>L: postMessage: profile_page_viewed
    L->>DL: push {event: "calendly", calendly_event: "...profile_page_viewed"}
    
    V->>C: Selects time slot
    C->>L: postMessage: date_and_time_selected
    L->>DL: push {event: "calendly", calendly_event: "...date_and_time_selected"}
    
    V->>C: Confirms booking
    C->>L: postMessage: event_scheduled
    L->>DL: push {event: "calendly", calendly_event: "...event_scheduled"}
    DL->>GTM: Fires GA4 + Google Ads conversion

GTM Setup Guide

Step 1: Create Custom HTML Tag

  1. GTM → Tags → Custom HTML
  2. Paste the code below
  3. Trigger: All Pages (Pageview)

Step 2: Create Triggers

Trigger NameEvent NameCondition
CE – Calendly Allcalendly
CE – Calendly Bookedcalendlycalendly_event contains event_scheduled
CE – Calendly Time Selectedcalendlycalendly_event contains date_and_time_selected

Step 3: Create Variable

Variable NameDL Key
DLV – Calendly Eventcalendly_event

Step 4: Attach Tags

  • CE – Calendly Booked → GA4 event book_appointment, Google Ads conversion, Meta Schedule event
  • CE – Calendly Time Selected → GA4 engagement event

Installation

html
<!-- GTM Custom HTML Tag: Calendly Listener -->
<!-- Trigger: All Pages (Pageview) -->
<script>
function isCalendlyEvent(e) {
  return e.origin === 'https://calendly.com' && e.data.event && e.data.event.indexOf('calendly.') === 0;
}

window.addEventListener('message', function(e) {
  if (isCalendlyEvent(e)) {
    window.dataLayer = window.dataLayer || [];
    window.dataLayer.push({
      'event': 'calendly',
      'calendly_event': e.data.event,
      'calendly_event_type_name': e.data.payload && e.data.payload.event_type && e.data.payload.event_type.name,
      'calendly_event_type_duration': e.data.payload && e.data.payload.event_type && e.data.payload.event_type.duration,
      'calendly_invitee_email': e.data.payload && e.data.payload.invitee && e.data.payload.invitee.email,
      'calendly_invitee_name': e.data.payload && e.data.payload.invitee && e.data.payload.invitee.name,
      'calendly_event_start_time': e.data.payload && e.data.payload.event && e.data.payload.event.start_time,
      'calendly_tracking_utm_source': e.data.payload && e.data.payload.tracking && e.data.payload.tracking.utm_source,
      'calendly_tracking_utm_medium': e.data.payload && e.data.payload.tracking && e.data.payload.tracking.utm_medium,
      'calendly_tracking_utm_campaign': e.data.payload && e.data.payload.tracking && e.data.payload.tracking.utm_campaign
    });
  }
});
</script>

Data Layer Output

Event Scheduled (Booking Confirmed)

json
{
  "event": "calendly",
  "calendly_event": "calendly.event_scheduled",
  "calendly_event_type_name": "30-Minute Demo",
  "calendly_event_type_duration": 30,
  "calendly_invitee_email": "jane@acmecorp.com",
  "calendly_invitee_name": "Jane Smith",
  "calendly_event_start_time": "2024-02-15T14:00:00Z",
  "calendly_tracking_utm_source": "google",
  "calendly_tracking_utm_medium": "cpc",
  "calendly_tracking_utm_campaign": "brand-terms"
}

Date/Time Selected

json
{
  "event": "calendly",
  "calendly_event": "calendly.date_and_time_selected",
  "calendly_event_type_name": "30-Minute Demo"
}

Variables to Capture

Variable NameDL KeyExample
DLV – Calendly Eventcalendly_event"calendly.event_scheduled"
DLV – Calendly Event Typecalendly_event_type_name"30-Minute Demo"
DLV – Calendly Invitee Emailcalendly_invitee_email"jane@acmecorp.com"
DLV – Calendly Invitee Namecalendly_invitee_name"Jane Smith"
DLV – Calendly UTM Sourcecalendly_tracking_utm_source"google"
DLV – Calendly Start Timecalendly_event_start_time"2024-02-15T14:00:00Z"

GA4 Mapping Recommendations

GA4 EventTriggerParameters
book_appointmentCalendly Bookedevent_type, email, duration
select_contentDate/Time Selectedcontent_type: "time_slot"
view_itemEvent Type Vieweditem_name: event type

Enhanced Conversions setup:

  • Use calendly_invitee_email as the user email in Google's User-Provided Data variable

Debugging

GTM Preview Mode

  1. Embed a Calendly widget on your site
  2. Open GTM Preview mode
  3. Go through the booking flow
  4. Check for calendly events at each step
  5. Verify calendly_event_scheduled fires on confirmation

Common Issues

ProblemCauseFix
No eventsOrigin check failsVerify Calendly loads from calendly.com
event_scheduled not firingUsing redirect (not embed)This listener is for embedded widgets only
Missing payload dataOlder Calendly API versionpayload is only in v2 postMessage format

Redirect vs. Embed

For Calendly redirect pages (user goes to calendly.com/yourname), use GTM on the Calendly thank you page URL parameter instead of this listener.

Best Practices

  1. Use Calendly's UTM passthrough, Calendly passes UTMs in the tracking payload. Capture all UTM parameters to attribute bookings to campaigns
  2. Hash the email for Enhanced Conversions, calendly_invitee_email is perfect for Google's Enhanced Conversions
  3. Per-event-type triggers, Create different conversion actions for "Demo" vs. "Support" vs. "Onboarding" calls
  4. Set up the booking funnel in GA4: Profile Viewed → Event Type Viewed → Time Selected → Booked

Performance Considerations

  • The isCalendlyEvent origin check prevents performance issues from processing all postMessages
  • Zero polling, pure event-driven architecture

Related Listeners

Example Business Scenarios

Scenario: SaaS Sales Funnel

A SaaS company embeds Calendly for "Book a Demo" on their pricing page. They track:

  1. calendly.profile_page_viewed → Demo intent (fire Meta ViewContent)
  2. calendly.date_and_time_selected → High intent (fire Meta InitiateCheckout)
  3. calendly.event_scheduled → Conversion (fire Google Ads, Meta Schedule, GA4 book_appointment)

They also capture calendly_tracking_utm_campaign to see which Google Ads campaigns drive the most demos.

FAQ

Q: Does this work with Calendly popup widgets? A: Yes, popup embeds use the same postMessage API.

Q: Does it work for multiple Calendly widgets on the same page? A: Yes, the listener captures all Calendly postMessages on the page.

Q: Can I capture the meeting notes the invitee enters? A: No, the payload only includes basic invitee info and event details, not form answers. Those are in the Calendly webhook.

Q: Does this work on Calendly's routing forms? A: Routing form events may use different event names. Test in Preview mode and inspect the e.data.event value.