Ads for Breakfast
← All posts

GTM template: track Calendly bookings as GA4 events

A free GTM container that turns every Calendly booking into a GA4 event — DataLayer listener, custom HTML tag, and the one condition that keeps your conversion count honest.

4 min read

Originally written in German — read it on visnakovs.de.

GTM template for tracking Calendly events in GA4

Calendly runs as an embed or a pop-up and sends no standard events to GA4 — so the bookings show up nowhere in your reports. This GTM template closes the gap and makes every Calendly booking measurable as a GA4 event.

What’s in the template

  • 1 custom HTML tag — Calendly event listener that watches for message events from the Calendly embed
  • 1 GA4 event tag — fires on Calendly events (booking started, booking completed)
  • 1 custom event trigger — the calendly event
  • 1 DataLayer variablecalendly_event, carrying the event type

How it works

Calendly talks to the parent page over the postMessage API. The custom HTML tag listens for those messages and pushes them into the DataLayer:

window.addEventListener('message', function(e) {
  if (e.data.event && e.data.event.indexOf('calendly') > -1) {
    dataLayer.push({
      event: 'calendly',
      calendly_event: e.data.event
    });
  }
});

The GA4 event tag fires on the calendly custom event and passes the event type through as a parameter.

The Calendly events you can track

  • calendly.profile_page_viewed — the Calendly widget loaded
  • calendly.event_type_viewed — the user looked at an appointment type
  • calendly.date_and_time_selected — date and time picked
  • calendly.event_scheduledthe appointment was booked — that’s your conversion

Setup in two minutes

1. Import the template

Import container → upload the JSON → merge.

2. Enter your GA4 measurement ID

Drop your G-XXXXXXX ID into the GA4 event tag.

3. Test it

  1. Turn on GTM preview mode
  2. Open the Calendly widget
  3. Book a test appointment
  4. Check: does the tag fire on calendly.event_scheduled?

Count only real bookings as conversions

Add a condition to the GA4 event tag so it only fires on calendly.event_scheduled. Then the actual booking counts as a conversion — not every widget view. Skip this and your conversion column quietly inflates with people who opened the calendar and left.

Download the template

The configurator that generates this container lives on my German site: the GTM configurator (in German) bundles all seven of my setups in one tool. The interface is German, the JSON it produces is language-neutral.

Copy the JSON below, save it as a .json file and import it into your Google Tag Manager container under Admin → Import Container.

No email, no form — just take it.

Show the GTM container JSON
{
    "exportFormatVersion": 2,
    "exportTime": "2023-05-25 13:50:28",
    "containerVersion": {
        "path": "accounts/0000000000/containers/000000000/versions/0",
        "accountId": "0000000000",
        "containerId": "000000000",
        "containerVersionId": "0",
        "container": {
            "path": "accounts/0000000000/containers/000000000",
            "accountId": "0000000000",
            "containerId": "000000000",
            "name": "Calendly GA4 recipe",
            "publicId": "GTM-XXXXXX",
            "usageContext": [
                "WEB"
            ],
            "fingerprint": "0000000000000",
            "tagManagerUrl": "https://tagmanager.google.com/#/container/accounts/0000000000/containers/000000000/workspaces?apiLink=container",
            "features": {
                "supportUserPermissions": true,
                "supportEnvironments": true,
                "supportWorkspaces": true,
                "supportGtagConfigs": false,
                "supportBuiltInVariables": true,
                "supportClients": false,
                "supportFolders": true,
                "supportTags": true,
                "supportTemplates": true,
                "supportTriggers": true,
                "supportVariables": true,
                "supportVersions": true,
                "supportZones": true
            },
            "tagIds": [
                "GTM-XXXXXX"
            ]
        },
        "tag": [
            {
                "accountId": "0000000000",
                "containerId": "000000000",
                "tagId": "3",
                "name": "cHTML - Calendly listener",
                "type": "html",
                "parameter": [
                    {
                        "type": "TEMPLATE",
                        "key": "html",
                        "value": "<script>\nwindow.dataLayer = window.dataLayer ||[];\nwindow.addEventListener('message',\n  function(e) {\n    if (e.data.event && e.data.event.indexOf('calendly') === 0) {\n      window.dataLayer.push({\n        'event' : 'calendly',\n        'calendly_event' : e.data.event.split('.')[1]\n      });\n    }\n  }\n);\n</script>"
                    },
                    {
                        "type": "BOOLEAN",
                        "key": "supportDocumentWrite",
                        "value": "false"
                    }
                ],
                "fingerprint": "0000000000000",
                "firingTriggerId": [
                    "2147479553"
                ],
                "tagFiringOption": "ONCE_PER_EVENT",
                "monitoringMetadata": {
                    "type": "MAP"
                },
                "consentSettings": {
                    "consentStatus": "NOT_SET"
                }
            },
            {
                "accountId": "0000000000",
                "containerId": "000000000",
                "tagId": "6",
                "name": "GA4 event - calendar events - calendly",
                "type": "gaawe",
                "parameter": [
                    {
                        "type": "BOOLEAN",
                        "key": "sendEcommerceData",
                        "value": "false"
                    },
                    {
                        "type": "TEMPLATE",
                        "key": "eventName",
                        "value": "calendar_{{dlv - calendly_event}}"
                    },
                    {
                        "type": "TEMPLATE",
                        "key": "measurementId",
                        "value": "none"
                    },
                    {
                        "type": "TEMPLATE",
                        "key": "measurementIdOverride",
                        "value": "{{INSERT YOUR MEASUREMENT ID HERE}}"
                    }
                ],
                "fingerprint": "0000000000000",
                "firingTriggerId": [
                    "4"
                ],
                "tagFiringOption": "ONCE_PER_EVENT",
                "monitoringMetadata": {
                    "type": "MAP"
                },
                "consentSettings": {
                    "consentStatus": "NOT_SET"
                }
            }
        ],
        "trigger": [
            {
                "accountId": "0000000000",
                "containerId": "000000000",
                "triggerId": "4",
                "name": "custom - calendly",
                "type": "CUSTOM_EVENT",
                "customEventFilter": [
                    {
                        "type": "EQUALS",
                        "parameter": [
                            {
                                "type": "TEMPLATE",
                                "key": "arg0",
                                "value": "{{_event}}"
                            },
                            {
                                "type": "TEMPLATE",
                                "key": "arg1",
                                "value": "calendly"
                            }
                        ]
                    }
                ],
                "fingerprint": "0000000000000"
            }
        ],
        "variable": [
            {
                "accountId": "0000000000",
                "containerId": "000000000",
                "variableId": "5",
                "name": "dlv - calendly_event",
                "type": "v",
                "parameter": [
                    {
                        "type": "INTEGER",
                        "key": "dataLayerVersion",
                        "value": "2"
                    },
                    {
                        "type": "BOOLEAN",
                        "key": "setDefaultValue",
                        "value": "false"
                    },
                    {
                        "type": "TEMPLATE",
                        "key": "name",
                        "value": "calendly_event"
                    }
                ],
                "fingerprint": "0000000000000",
                "formatValue": {}
            }
        ],
        "fingerprint": "0000000000000",
        "tagManagerUrl": "https://tagmanager.google.com/#/versions/accounts/0000000000/containers/000000000/versions/0?apiLink=version"
    }
}

If your booking tracking is guessing rather than measuring — or you want a second pair of eyes on the setup before you trust the numbers — write to me.