How to track UTM parameters in GoHighLevel: set up custom fields, workflow logic & live attribution reports to prove which campaigns drive revenue in 2026.
You've already decided UTM tracking is the answer. Now you need the exact implementation inside GoHighLevel — the custom fields, the workflow logic, the reporting setup, and the fixes when it breaks. That's what this guide covers, start to finish.
Here's the scenario we see every week: a roofing client spends $4,000 on Google Ads in a month, six leads come in through a GHL funnel, and every single contact record shows "Source: direct." The client asks which keywords are working. You have nothing.
UTM parameters are the five query string values you append to any URL to tell your analytics system exactly where traffic came from. They are: utm_source (the platform — Google, Facebook), utm_medium (the traffic type — cpc, email), utm_campaign (the specific campaign name), utm_term (the keyword or targeting), and utm_content (the specific creative or ad variant). When these travel through to a form submission and get stored on a contact record, you have a complete audit trail from ad click to closed deal.
GHL's default attribution captures some of this — but not reliably across every channel and funnel configuration. The gap between "GHL captures something" and "you can filter contacts by campaign and see revenue in the pipeline" is exactly what this guide closes.
By the end, you'll have a fully operational UTM tracking and attribution system inside GoHighLevel: clean naming conventions, custom field mapping, first-touch/last-touch workflow logic, and live Smart List reports that answer the question "which campaign made us money this month."
GHL does capture UTM data out of the box — but with constraints that will burn you if you don't understand them.
Every GHL-hosted funnel and website has built-in URL parameter detection. When a visitor lands on a GHL page with UTM parameters in the URL, GHL reads those values and stores them in a session cookie. On the Contact record, scroll down to the Attribution panel — you'll find fields for Session Source, Session Medium, and Session Campaign, recorded at both first touch and last touch.
Screenshot description: A GHL Contact record with the Attribution panel expanded, showing "Session Source: google," "Session Medium: cpc," and "Session Campaign: roofing-leads-summer-2026" populated at first touch.
GHL's native capture only works reliably when the landing page is GHL-native. If you're running traffic to a WordPress or Webflow page with a GHL embed form, UTMs do not automatically pass through to the contact record unless you've installed the GHL tracking script on that external page.
GHL also doesn't map session-level UTM data to permanent custom fields automatically. The Attribution panel data is useful for individual contact audits, but it's not filterable as a Smart List, not triggerable in workflows, and not exportable in a structured way for client reporting.
GHL's built-in Attribution Report exists at the higher plan tiers. It gives you session-level traffic source breakdowns — helpful for trend spotting, but it uses session data rather than stored contact-level fields. We'll cover when to trust which data source in Section 11.
Don't assume GHL's Attribution panel is populating just because you see it on one contact. Test every funnel configuration by submitting a test form with a UTM-tagged URL and verifying the Attribution panel populates. We've seen sub-accounts where the toggle was never enabled and hundreds of leads had blank attribution — months of data gone.
No tracking system survives inconsistent naming. Before you touch a single GHL setting, lock down your UTM convention.
Start at ga-dev-tools.google.com/campaign-url-builder. Enter your destination URL and fill in each parameter. It generates the full tagged URL — no manual string-building, no typos.
Copy this table into a shared Google Sheet and give access to every team member and client before a single ad goes live.
| Parameter | Approved Values | Example |
|---|---|---|
utm_source |
google, facebook, instagram, tiktok, email, organic-social, referral | google |
utm_medium |
cpc, paid-social, email, organic, referral | cpc |
utm_campaign |
kebab-case brand slug + date | roofing-leads-summer-2026 |
utm_term |
keyword slug (search campaigns) | roofing-contractor-dallas |
utm_content |
creative identifier | video-testimonial-v1 |
Use lowercase everywhere. Use hyphens, not underscores or spaces. Never capitalise mid-string.
In our experience, naming convention failures account for the majority of "UTM tracking isn't working" complaints we troubleshoot. The GHL setup is fine — the data going in is garbage.
Document this in a Google Sheet with a tab per client sub-account. Include a column for approved campaign slugs so no one invents a new format mid-flight.
Add a UTM validation column in your Google Sheet using a simple formula that checks for uppercase letters or spaces. Flag any non-compliant value before it goes into a live ad. Catching it upstream saves hours of data cleaning downstream.
This is the most commonly missed step. You can have perfect UTM naming and nothing will stick if this toggle is off.
Inside GHL, navigate to your Funnel or Website > click Settings (the gear icon in the top right of the builder) > General > scroll to find "Track UTM Parameters" and switch it on. Save.
This toggle tells GHL to read URL parameters when a visitor lands on the page and store them in the session cookie. Without it, GHL ignores UTMs entirely — even if they're in the URL.
Once enabled, GHL reads the UTM values from the landing page URL and persists them through multi-step funnels within the same session. A visitor hits page 1 with ?utm_source=google&utm_medium=cpc, clicks through to page 2 (no UTMs in the URL), submits the form on page 3 — GHL still passes the original UTM values to the form submission.
If your ad sends traffic to an external page, the toggle above does nothing for that page. Install the GHL tracking script on every external page — find it under Settings > Business Profile > Tracking Code. Copy the script and paste it into the <head> of every page on your external site.
Screenshot description: The GHL Business Profile Tracking Code section showing the embedded JavaScript snippet to copy, and the Funnel Settings panel showing the "Track UTM Parameters" toggle in the enabled (blue) state.
GHL's session cookie is domain-scoped. If your ad sends traffic to yourdomain.com (external) and then redirects to yourfunnel.gohighlevel.com, the UTM cookie from the first domain does not carry over to the GHL subdomain.
Cross-domain redirects silently kill UTM attribution. If a visitor lands on your WordPress site and you redirect them to a GHL funnel subdomain, start your UTM chain over — or better, keep the entire funnel on one domain. We moved three client funnels from hybrid WordPress-to-GHL setups to fully GHL-hosted after losing two months of attribution data to this exact issue.
Keep the full conversion funnel — ad landing page through form submission — on a single domain whenever possible.
Session-level UTM capture is temporary. Custom field mapping makes UTMs permanent, filterable, and reportable at the contact level.
GHL's Attribution panel shows you what it captured for a given contact, but you can't build a Smart List filtered by "Attribution Session Source = google." You can only filter by custom fields. Mapping UTMs to custom fields is what transforms session data into contact data — and contact data is what powers every workflow, filter, and export in GHL.
| Field Name | Field Type |
|---|---|
| UTM Source | Text |
| UTM Medium | Text |
| UTM Campaign | Text |
| UTM Term | Text |
| UTM Content | Text |
| Landing Page URL | Text |
| GCLID | Text |
| FBCLID | Text |
utm_source) and use the "Map to Custom Field" dropdown to link it to the corresponding custom field you just created.{{utm_source}}, {{utm_medium}}, {{utm_campaign}}, {{utm_term}}, {{utm_content}}, {{landing_page_url}}, {{gclid}}, {{fbclid}}.Screenshot description: The GHL Form builder showing a hidden field element with "Parameter Name" set to "utm_source" and the "Map to Custom Field" dropdown set to "UTM Source," with the default value field showing "{{utm_source}}".
Forms embedded on external pages via GHL's embed code will not auto-populate hidden fields unless the GHL tracking script is installed on that external page. The tracking script must load before the form for parameter injection to work. Verify this with a test submission every time you deploy to a new external domain.
Hidden field mapping gets you most of the way there. Workflows close the remaining gap and add the first-touch/last-touch logic that makes attribution accurate across repeat contacts.
Set the trigger to Form Submitted (or Survey Submitted) and filter by the specific form name receiving paid traffic leads.
Action 1 — Write Last Touch UTM data:
Add an "Update Contact Field" action. Set UTM Source (Last Touch) to the merge tag {{contact.utm_source}}. Repeat for UTM Medium, Campaign, Term, and Content. These always overwrite — they record what channel sent this specific submission.
Action 2 — Preserve First Touch with IF/ELSE logic: Add an "If/Else" branch immediately after the last-touch writes.
UTM Source (First Touch) is emptyUTM Source (First Touch) to {{contact.utm_source}}. Repeat for all five UTM fields.Never run an "Update Contact Field" action for first-touch UTM data without the IF/ELSE branch. Every subsequent form submission from the same contact will overwrite their original source. We've seen this wipe three months of first-touch data from a client's entire contact list — it's a silent, irreversible error.
Inside GHL email campaigns, use Trigger Links (found under Marketing > Trigger Links). Assign a name to each link and append UTM parameters to the destination URL. GHL logs the Trigger Link click server-side before redirecting the user, so attribution fires even if the browser strips the UTM from the final URL.
First-click attribution records the channel that brought a lead into your world for the first time. Last-click attribution records the channel active when they finally submitted a form and became a contact.
Both numbers tell you something different. First-click shows you what's building awareness at the top of your funnel — typically branded search, social ads, or content. Last-click shows you what's closing — often branded search, email retargeting, or direct. If you only track one, you misread your funnel.
Google Ads has moved away from pure last-click as its default model precisely because last-click undervalues the upper-funnel channels that started the journey. The same logic applies inside GHL.
Build two versions of each UTM field:
| Field Name | Logic | When It's Written |
|---|---|---|
| UTM Source (First Touch) | Write once, never overwrite | First form submission only (IF/ELSE = empty) |
| UTM Source (Last Touch) | Always overwrite | Every form submission |
| UTM Medium (First Touch) | Write once, never overwrite | First form submission only |
| UTM Medium (Last Touch) | Always overwrite | Every form submission |
| UTM Campaign (First Touch) | Write once, never overwrite | First form submission only |
| UTM Campaign (Last Touch) | Always overwrite | Every form submission |
Apply this same pattern to UTM Term and UTM Content for complete dual-field coverage.
Here's the exact scenario we use to explain this to every new client: a lead clicks a Google Ads search ad for "roofing contractor Dallas" — that's first touch. They don't book. Two weeks later, they receive a GHL email retargeting sequence, click through, and submit the quote form. Last touch is email.
Without dual-field attribution, you credit email for the conversion and consider cutting your Google Ads budget. With dual-field attribution, you see that Google Ads sourced the lead and email closed it — and you keep both channels running.
In our experience managing paid traffic attribution across GHL sub-accounts, approximately 40% of converted contacts show a different first-touch source than last-touch. Relying on last-click alone misattributes nearly half of all conversions.
Navigate to Settings > Integrations > Google Ads and connect your Google account. Once connected, create a Conversion Action inside GHL (give it a name matching the action you want to track, such as "Quote Form Submitted"). Then inside your workflow — triggered by the form submission — add the "Google Ads: Report Conversion" action and select your conversion action.
GHL fires the conversion event to Google Ads when the workflow action runs. This is the fastest setup and works well for straightforward single-funnel accounts.
Google appends a GCLID (Google Click Identifier) to every ad URL at the moment of the click. This value is machine-readable — it ties a specific click back to a specific keyword, ad, and bidding auction inside Google's systems.
You created the GCLID custom field in Step 3. That field captures the GCLID from the form's hidden field mapped to parameter gclid. Once it's stored on the contact record, you have two paths:
Run both the native GHL integration AND GCLID capture simultaneously. The native integration gives Google real-time conversion signals for bidding. The GCLID export gives you verified revenue conversions after the deal closes — a far more accurate signal for Smart Bidding optimisation. UTMs tell your team which campaign performed; GCLIDs tell Google's algorithm which clicks to buy more of.
Google's Enhanced Conversions take this further by hashing first-party contact data (email, phone) and matching it to logged-in Google users — worth enabling if your plan supports it.
Meta attribution has three distinct scenarios, and each requires a different approach. Most guides cover only one.
This is the attribution black hole most agencies don't notice until they've run campaigns for months. When you run Facebook Lead Ads, the user fills out a form inside Meta — there is no landing page, no URL, no UTM parameters to capture.
The workaround: use Meta's Dynamic Ad Parameters in the "Website URL" field of your Lead Ad setup. Append this string to whatever URL you enter:
?ad_name={{ad.name}}&adset_name={{adset.name}}&campaign_name={{campaign.name}}
Meta dynamically populates these values at impression time. When the lead syncs to GHL via the native Facebook Lead Ads integration, these parameter values appear in the lead data. Map them to custom fields labelled "Ad Name," "Ad Set Name," and "Campaign Name" — they won't match standard UTM field names, so create dedicated fields for Meta Lead Ad data.
Standard UTM capture works as described in Steps 2–4. Tag your Meta ad destination URLs with UTMs using the URL builder, enable the GHL toggle, and let the workflow stamp the contact. No special configuration required.
Facebook appends an FBCLID (Facebook Click Identifier) to outbound ad URLs automatically. You created the FBCLID custom field in Step 3 — the hidden form field mapped to parameter fbclid captures it.
Store the FBCLID on every contact who clicks a Meta ad. This value is the key for Meta Conversions API (CAPI) offline event matching. Inside GHL, go to Settings > Integrations > Facebook to connect Meta CAPI. With CAPI enabled, GHL sends conversion events server-side using first-party contact data (email, phone, FBCLID), which restores attribution accuracy that browser-based pixel tracking loses to ad blockers and privacy restrictions.
For any client running Meta Lead Ads, set up a dedicated Smart List filtered by "Campaign Name (Meta)" to segment those leads separately from funnel-based Meta traffic. These are two different lead qualities, often with different close rates — reporting them together hides the performance difference.
Apple's iOS 17 introduced Link Tracking Protection in the Mail app and Safari. This strips UTM parameters — and other tracking identifiers — from URLs before the user's browser loads them. If you're sending email campaigns through GHL with UTM-tagged links, iOS 17 users clicking in Apple Mail may arrive at your funnel with clean URLs and zero attribution.
Use GHL Trigger Links for all UTM-tracked links in email and SMS campaigns. Trigger Links use GHL's own redirect domain as an intermediary. The click registers in GHL server-side (before the redirect fires), so attribution is logged regardless of what the browser strips. The user ends up at the destination URL — you keep the click data.
UTM parameters in a URL survive when a user copies a link and opens it on a different device, shares it with a spouse, or opens it days later in a different browser. Cookie-based tracking breaks in all three scenarios. Building your attribution on UTM custom fields stored at the contact level gives you a persistent data record that doesn't expire.
Do not treat Meta Pixel or Google Tag browser-based events as your only conversion tracking signal. In our builds, we consistently see a 15–25% undercount in browser-based pixel conversions compared to server-side + custom field data combined. Supplement pixels with the UTM custom field data stored directly on the GHL contact.
Grab the Free GoHighLevel UTM Tracking Setup Checklist
Get the exact custom fields, workflow triggers, and naming conventions used in this guide — pre-formatted and ready to import into your GHL account.
Written by Tim Hershberger, founder of Automate the Journey. Tim has built 500+ marketing automation systems for service businesses since 2009. Book a free strategy call to see how we can help.
Everything in this guide runs on GoHighLevel. Try it free for 30 days and see why we chose it.
No credit card required · Cancel anytime