If you are using GoHighLevel forms or calendar widgets on your website, one of the most common issues is that standard Google Tag Manager form triggers do not work reliably.
Why?
Because most GoHighLevel embeds run inside iframes and communicate through browser messages instead of native HTML form submissions.
That means if you rely on GTM’s built-in form submission trigger, you will likely miss conversions.
In this guide, I’ll show you how to properly track GoHighLevel form submissions and booking completions using an event listener.
Why GoHighLevel Tracking Requires a Custom Listener
Unlike regular website forms, GoHighLevel embedded forms and calendars:
- Run inside an iframe
- Do not trigger browser form submit events
- Send submission data through postMessage
Because of that, GTM cannot detect successful submissions out of the box.
The solution is to listen for those iframe messages manually and push them into the dataLayer.
What This Setup Tracks
With this listener, you can capture:
- Successful GoHighLevel form submissions
- Booking completions from calendar widgets
- Submitted lead information
- Customer ID and contact details
This gives you clean conversion events for analytics and ad platforms.
Step 1: Add the GoHighLevel Event Listener
Create a Custom HTML Tag in GTM and add the script below.
Fire it on All Pages or DOM Ready.
<script>
(function () {
var cachedInputData = {};
window.dataLayer = window.dataLayer || [];
window.addEventListener(‘message’, function (event) {
if (Object.prototype.toString.call(event.data) !== ‘[object Array]’) {
return;
}
var inputJSON = event.data[2];
if (
inputJSON &&
typeof inputJSON === ‘string’ &&
inputJSON.includes(‘full_address’) &&
inputJSON.includes(‘customer_id’) &&
inputJSON.includes(‘full_name’) &&
inputJSON.includes(’email’)
) {
try {
var parsedInputs = JSON.parse(inputJSON);
cachedInputData = parsedInputs;
dataLayer.push({
event: ‘ghl_form_submit’,
inputs: parsedInputs
});
} catch (err) {
console.error(‘GHL Listener JSON Parse Error:’, err);
}
}
if (event.data[0] === ‘msgsndr-booking-complete’) {
dataLayer.push({
event: ‘ghl_booking_complete’,
inputs: cachedInputData
});
}
});
})();
</script>
How the Script Works
Let’s break down what happens behind the scenes.
It Listens for GoHighLevel Messages
The script waits for iframe messages sent from the embedded GoHighLevel widget.
It Detects Form Submission Payloads
When the message contains lead data such as:
- Full name
- Address
- Customer ID
it treats that as a successful form submission.
Then it pushes:
event: ‘ghl_form_submit’
to the dataLayer.
It Tracks Booking Completion Separately
When GoHighLevel sends:
msgsndr-booking-complete
the script pushes:
event: ‘ghl_booking_complete’
It Preserves Lead Data for Booking Events
The submitted form inputs are cached temporarily so the booking completion event can include them as well.
This is useful when a user fills out details before booking an appointment.
Step 2: Create GTM Custom Event Triggers
You need two triggers.
Form Submission Trigger
Trigger Type: Custom Event
Event Name:
ghl_form_submit
Booking Completion Trigger
Trigger Type: Custom Event
Event Name:
ghl_booking_complete
Step 3: Create Data Layer Variables
Because the input data is nested inside the inputs object, create variables using dot notation.
Examples:
| Variable Name | Data Layer Variable |
| inputs.email | |
| Name | inputs.full_name |
| Customer ID | inputs.customer_id |
Step 4: Send Conversion Events to Your Platforms
Now you can use these triggers to fire:
For Form Submissions
- GA4 Lead Event
- Google Ads Conversion
- Meta Lead Event
For Booking Completions
- GA4 Schedule Appointment Event
- Qualified Lead Conversion
- Meta Schedule Event
Step 5: Test the Setup
Open GTM Preview Mode and test:
- Submit a GoHighLevel form
- Complete a booking
You should see:
ghl_form_submit
ghl_booking_complete
Verify that the inputs object contains the expected user data.
Common Mistakes to Avoid
Using Native Form Submission Trigger
This usually does not work for GoHighLevel iframe embeds.
Not Loading the Listener Early Enough
Load the script on:
- DOM Ready
or - All Pages
Forgetting Nested Data Layer Paths
Remember:
inputs.email
not just:
Final Thoughts
Tracking GoHighLevel forms and bookings properly requires more than GTM’s default triggers.
Because the platform relies heavily on iframe embeds and postMessage communication, a custom listener is the most reliable way to capture conversions.
Once implemented, you get:
- Accurate lead tracking
- Reliable booking attribution
- Full submitted user data for enhanced conversions
- Better optimization signals for your ad platforms
If you are running paid traffic to GoHighLevel forms or calendars, this setup is essential for trustworthy reporting.
