This is for agencies and service businesses running ads into GoHighLevel funnels, forms and calendars. The usual problem: the ad platforms see form fills, but nobody can tell which campaign produced the booked call or the signed client. I connect GHL's front end and its pipeline to GA4, Google Ads and Meta so each lead can be followed through to revenue.
GHGoHighLevel · Lead generation
Form, survey and booking events tracked even when embedded in iframes
Pipeline stage changes sent back to Google Ads and Meta as offline conversions
Proper GoHighLevel tracking has two layers. On the front end, GTM on your funnels and sites captures form, survey and calendar conversions with the click ID. In the back end, workflows fire webhooks when an opportunity moves stage, sending qualified leads and won deals to Google Ads offline conversions and the Meta Conversions API.
Tracking flow
How tracking works on GoHighLevel
The events I set up on GoHighLevel, and where each one is sent.
01GoHighLevel siteVisitors browse, sign up and buy
03Google Tag ManagerOne web container for all tags
04Server-side GTMOn your own subdomain
Sent to
GA4Ecommerce and lead events
Google AdsConversions, incl. offline
MetaPixel + Conversions API
Why it breaks
Why GoHighLevel tracking goes wrong
Embedded forms and calendars live in an iframe
When a GHL form or calendar is embedded on WordPress or another site, it loads from a different domain inside an iframe. The parent page's tags can't see the submission, so the conversion either goes missing or gets counted on the wrong page.
Tracking code pasted in the wrong place
GHL lets you add code at the funnel or website level, on individual pages and in custom code elements. I often find the same pixel loaded in two of those places, which double-counts leads, or missing from the thank-you step entirely.
Click IDs never reach the contact
Offline conversions only work if the gclid, gbraid or fbclid from the ad click is stored with the contact. If a redirect, a multi-step funnel or a form without hidden fields drops it, the deal can't be matched back to the ad.
Ads optimise on form fills, not real outcomes
Most accounts only send the first opt-in to Google Ads and Meta. Leads that never show up or never buy count the same as clients who pay, so bidding learns to chase cheap, low-quality leads.
Native integrations with limited control
GHL's built-in Google Ads and Meta connections are a reasonable start, but you get limited control over which fields are sent, how events are named and how they deduplicate against the browser pixel. Mixing them with a custom setup without a plan causes duplicates.
What I set up
My GoHighLevel tracking setup
Funnel, site & form tracking
GTM installed once at the funnel/website level, with old duplicate pixels removed
Form and survey submission events with form name and step
Calendar booking events from the booking confirmation
Iframe-embedded forms and calendars tracked on external sites
Hidden fields that save gclid, fbclid and UTMs to the contact
Pipeline & offline conversions
Workflows triggered by opportunity stage and status changes
Webhooks that send qualified, booked and won stages to server-side GTM
Google Ads offline conversion uploads matched on click ID
Enhanced conversions for leads using hashed email and phone
Deal value passed with won opportunities
Meta Pixel & Conversions API
Pixel Lead and Schedule events on funnel pages
Server-side CAPI events from workflows for later pipeline stages
Shared event IDs so browser and server events deduplicate
fbp/fbc and contact data sent for better event matching
Agency reporting
GA4 key events that match the conversions you bid on
Lead-to-sale view by campaign, ad set and keyword
A documented template you can roll out to new sub-accounts
Consent Mode v2 set up where your audience requires it
Handover notes for your team
Event map
Events I track on GoHighLevel
Event
When it fires
Sent to
generate_lead
Funnel or website form submitted
GA4, Google Ads, Meta Pixel + CAPI
survey_complete
Multi-step survey finished
GA4, Meta Pixel + CAPI
book_appointment
Calendar booking confirmed
GA4, Google Ads, Meta (Schedule)
qualified_lead
Opportunity moved to a qualified stage (workflow webhook)
Google Ads offline, Meta CAPI
appointment_showed
Appointment status set to showed
Google Ads offline, Meta CAPI
closed_won
Opportunity marked won, with deal value
Google Ads offline, Meta CAPI (Purchase), GA4
phone_click
Click on a tel: link on a funnel page
GA4, Google Ads
Process
How the setup works
01
Audit
I review your funnels, sites, forms, calendars and existing integrations, and list what is double-counted, missing or unattributed.
02
Map the pipeline
We agree which pipeline stages count as a real conversion and what each one is worth, so the ad platforms learn from the outcomes you care about.
03
Build front-end tracking
I set up GTM, form, survey and booking events, and the hidden fields that save click IDs and UTMs to each contact.
04
Connect workflows
I build the workflows and webhooks that send stage changes through server-side GTM to Google Ads and the Meta Conversions API.
05
Test and hand over
I run test leads through the whole journey, check them in each platform's diagnostics, and document the setup so it can be copied to other sub-accounts.
Why GoHighLevel needs offline conversion tracking
In GoHighLevel the real value of a lead shows up later, in the pipeline: a booked call, a show, a signed contract. If Google Ads and Meta only ever hear about the opt-in, they treat a tyre-kicker and a paying client the same way.
GHL workflows can react to an opportunity changing stage or status and fire a webhook. I point those webhooks at a server-side GTM container (on Stape or Google Cloud), which formats the data and sends it to Google Ads as an offline conversion and to Meta as a Conversions API event. It's the same idea as my CRM and marketing automation work at /services/crm-marketing-automation/, applied to GHL's pipeline.
Lead-to-sale attribution for agencies
Agencies managing several sub-accounts need a setup they can repeat, not a one-off tangle of snippets. I build a standard container, naming convention and set of workflows that can be adapted per client, so every account reports leads, bookings and closed deals the same way.
The result is that you can show a client which campaigns brought in revenue, not just leads, and give the ad platforms better signals to optimise on.
FAQ
GoHighLevel tracking FAQ
Can GoHighLevel send offline conversions to Google Ads?
Yes. GHL has a native Google Ads connection, and you can also use workflow webhooks to send stage changes through server-side GTM or the Google Ads API. Either way, the contact needs a stored gclid (or gbraid/wbraid) or hashed contact details so Google can match the conversion to a click.
How do I track a GHL form embedded on my WordPress site?
The embedded form runs in an iframe on GHL's domain, so your site's GTM can't see the submit directly. Depending on the form type, I either listen for messages the iframe sends to the parent page (where GHL provides them), redirect to a thank-you page you control, or track the submission from GHL's side with a workflow. The right option depends on how the form is embedded.
Should I use GHL's built-in Meta Conversions API or a custom setup?
The built-in option is fine for basic lead events. A custom setup through server-side GTM gives you control over event names, deal values, matching data and deduplication with the browser pixel. What matters most is not running both for the same event without shared event IDs.
Can I track calendar bookings as conversions?
Yes. I track the booking on the confirmation step as a front-end event, and send later appointment statuses such as showed through a workflow. That way you can optimise for booked calls, or for calls that actually happened.
Will this work across multiple sub-accounts?
Yes. I build the tracking as a documented template, a GTM container plus a standard set of workflows, that can be adjusted for each sub-account. Each client still needs its own ad account IDs and conversion actions.
Do I need server-side tracking for GoHighLevel?
For pipeline events, effectively yes: stage changes happen in the CRM, not in the browser, so they have to be sent server to server. Server-side GTM also makes browser lead events more reliable when ad blockers or browser privacy limits block the pixel.