Guides · Calendly

Calendly UTM tracking for Google Ads, Meta & GTM

Get paid-media UTMs onto every Calendly booking, then fire clean event_scheduled conversions in Google Tag Manager — without drowning the dataLayer in iframe noise.

Calendly sits in an iframe. That creates two separate problems marketers mix up constantly:

  1. Source attribution — which ad / campaign produced the booking? Calendly only stores five UTM keys on the invitee.
  2. Conversion events — when did someone actually schedule? The embed fires many postMessage events (widget load, page height, duplicates). A naïve listener can push 6–10 dataLayer hits when you only needed three.

This guide solves both. Copy-ready ad recipes live below; the GTM section covers the listener problem discussed in r/GoogleTagManager.

What Calendly actually stores

Per Calendly Help, invitees keep only:

Each value must be under 255 characters. There is no native field for gclid, fbclid, or custom click IDs — map those into a UTM key if you need them inside Calendly exports, or keep them on the page for Google Ads / Meta / Enhanced Conversions.

Step 1 — Put UTMs on the ad click

Your scheduling page must receive the five keys in the URL when someone lands from paid media. On Google Ads, that usually means a Final URL suffix (so a redirect click tracker can still own Tracking template).

Google Ads example (Final URL suffix):

utm_source=google&utm_medium=cpc&utm_campaign={campaignid}&utm_term={keyword}&utm_content={creative}

Step 2 — Pass page UTMs into the Calendly embed

Landing-page query parameters do not automatically appear on the Calendly invitee. You must hand them to the embed.

Option A — Append UTMs on data-url

Fine for static sources (one embed per channel). Brittle for paid media.

<div class="calendly-inline-widget"
  data-url="https://calendly.com/YOUR_LINK/30min?utm_source=google&utm_medium=cpc"
  style="min-width:320px;height:630px;"></div>

Option B — Pull UTMs from the page URL (recommended for ads)

Reuse one embed across campaigns. Calendly’s advanced JS embed:

function getUrlVars() {
  const vars = {};
  window.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi, function (_m, k, v) {
    vars[k] = decodeURIComponent(v);
  });
  return vars;
}
const utms = getUrlVars();
Calendly.initInlineWidget({
  url: 'https://calendly.com/YOUR_LINK/30min',
  parentElement: document.getElementById('calendly-embed'),
  utm: {
    utmSource: utms.utm_source,
    utmMedium: utms.utm_medium,
    utmCampaign: utms.utm_campaign,
    utmContent: utms.utm_content,
    utmTerm: utms.utm_term,
  },
});

After a test booking, open the invitee in Calendly and confirm the five UTM fields match the ad click.

Step 3 — Fire clean booking events in GTM

Calendly posts messages from the iframe for more than bookings. A standard “Message” / custom event listener often catches widget loads, height changes and duplicates — so GA4 DebugView looks like a slot machine and conversion counts inflate.

Practitioners typically need only a small set, for example:

Practical GTM pattern

  1. Listen for Calendly postMessage events (custom HTML or a community GTM template).
  2. Filter at the listener: ignore height / resize / unknown events.
  3. Deduplicate per page load (in-memory set of event names already pushed).
  4. Push a clean dataLayer object, e.g. { event: 'calendly_event_scheduled', … }.
  5. Trigger a GA4 event tag (and optionally Google Ads conversion) off that single event name.

Do not rely on UTMs alone for Google Ads offline or enhanced conversions — keep gclid on the page or map it into utm_content with the gclid recipe when Calendly exports must carry the click ID.

Common failure modes

Copy-ready recipes

← All guides · Calendly recipes · Google Ads parameters