← GoHighLevel Review / Lead Source Tracking
Feature Guide · Updated August 2026

GoHighLevel Lead Source Tracking: Knowing Where Leads Actually Came From

What the built-in Contact Source field gives you, what it can't tell you, and the five-minute setup that fixes the gap.

Affiliate disclosure: This page contains affiliate links. If you sign up through them we may earn a commission at no extra cost to you.
TL;DR: GoHighLevel sets a Contact Source on every contact automatically, but it only records the mechanism (which form, calendar, lead ad or phone call), not the campaign. To get campaign-level tracking you add custom fields for your UTM values plus hidden form fields that read them from the URL, and a workflow that applies a SRC- tag. The contact record also has a first-click and last-click attribution panel, which only works where GoHighLevel's tracking sees the visit. The honest limit: ad reporting shows spend for paid channels only and never joins it to the per-contact source data, so blended cost per customer still gets built outside the platform.

What "lead source" means in GoHighLevel

Every contact record carries a Contact Source field, and GoHighLevel fills it in automatically based on how that contact entered the system. Typical values are the name of the form or survey submitted, a calendar booking, a Facebook or Instagram lead ad, an inbound call or SMS, a manual entry, a bulk import, or an API or integration. You'll find it on the contact record alongside the other standard fields, and you can filter and build smart lists on it.

It is genuinely useful and it is also the single most over-promised feature in GoHighLevel marketing. Contact Source answers "how did this person get in?" It does not answer "which ad did they click?" Those are different questions, and the second one is the one that decides where your budget goes next month.

The five ways to track source, and what each one is for

MethodWhat it capturesSetup effortUse it for
Contact Source fieldThe entry mechanism (form name, lead ad, call, import)None, automaticBasic channel split, sanity checks
UTM values in custom fieldsExact source, medium, campaign, ad or keyword~15 minutes, onceReal campaign-level reporting
Attribution panelFirst-click and last-click session source, referrerNone on GHL pages, code needed elsewhereSeeing the full path, not just the last step
Dedicated tracking numbersWhich channel produced a phone callOne number per channelOffline, print, GBP and direct-mail attribution
Source tagsA short label you controlOne workflowFast filtering and triggering automation

Setting up campaign-level tracking (the part that matters)

The fix is a one-time setup: create custom fields for your UTM parameters, then add hidden fields to your forms that read those parameters out of the URL. When someone lands on yoursite.com/offer?utm_source=google&utm_campaign=roof-repair and submits, those values write straight onto the contact record and stay there for the life of the lead.
  1. Create the custom fields. Settings → Custom Fields → add single-line text fields for utm_source, utm_medium, utm_campaign and utm_content. Full walkthrough: GoHighLevel custom fields.
  2. Add hidden fields to the form. Open the form or survey in the builder, add a hidden field for each one, and point each at the matching query parameter.
  3. Apply a source tag in a workflow. Trigger on the form submission and add a tag like SRC-GoogleAds. Tags are what you'll actually filter and automate on day to day. Convention guidance: GoHighLevel tags.
  4. Tag every link you publish. Ads, email links, social bios, QR codes. If the link isn't tagged there is nothing for the hidden field to capture, and this is where almost every setup quietly fails.
  5. Build the reports. Smart lists filtered by source for lead counts, and opportunities grouped by source for revenue, which is the number that actually matters.
  6. Test it. Submit your own form through a tagged URL and check the values landed before you make a single budget decision on the data.

The attribution panel, and its one big condition

Contact records include an attribution panel with first-click and last-click detail: session source, medium, campaign and the referring URL. It's the closest thing to real attribution in the platform, and it's free. The condition attached to it is the part people miss: it depends on GoHighLevel's tracking observing the visit, so it's reliable on GoHighLevel-hosted funnels, websites and forms, and unreliable to empty when your form is embedded on an external site without the tracking code installed.
Practical consequence: if you run a WordPress site with GoHighLevel forms embedded, install the tracking code on WordPress or accept that attribution will be thin. This is one of the real trade-offs in the WordPress hybrid setup, and it's worth deciding deliberately rather than discovering later.

Tracking phone leads

Calls are the blind spot in most lead-source setups, and the fix is dedicated numbers. Buy one GoHighLevel number per channel, put a different one on your Google Business Profile, your website, your ads and your print material, and the number dialled tells you the channel. Every call logs against the contact record and can trigger workflows, including missed call text back so an unanswered attributed lead doesn't just evaporate.

Numbers cost roughly $1.15/month each at the time of writing, so channel-level call attribution is cheap. US texting from those numbers still requires A2P 10DLC registration first.

What GoHighLevel will not tell you

The gap isn't spend data, it's the join between spend and your pipeline. GoHighLevel's ad reporting can connect Google Ads and Facebook Ads accounts and show spend next to lead counts for those channels, so paid cost per lead is available. What it won't do is tie that spend to the contact-level source data, so there's no blended cost per customer across every channel, and no spend figure at all for organic, referral, email or offline sources. Building the one number a business owner actually asks for still happens in a spreadsheet.

None of this makes the tracking useless. It makes it a lead-attribution system with paid-channel reporting bolted alongside, rather than a full ad-analytics platform, and knowing which one you bought saves an argument with a client later.

Do this first if you do nothing else: add UTM parameters to your ad links and one hidden field for utm_source on your main form. That single change turns "we got 40 leads" into "we got 40 leads and 31 came from one campaign," which is usually enough to change where the next month's budget goes.
Try GoHighLevel Free →Extended 30-day trial · set up source tracking on your first form

Frequently asked questions

What is lead source in GoHighLevel?

The Contact Source field on each contact, set automatically from how they entered: form name, calendar booking, lead ad, inbound call, manual entry, import or API. It records the mechanism, not the campaign.

How do I track which campaign a lead came from?

UTM parameters on your links, custom fields for utm_source, utm_medium and utm_campaign, hidden form fields that read them from the URL, plus a workflow that adds a SRC- tag.

Does it show first-click and last-click attribution?

Yes, in the attribution panel on the contact record. It relies on GoHighLevel's tracking seeing the visit, so external embedded forms need the tracking code installed.

Can it show cost per lead by source?

For paid channels yes, ad reporting connects Google Ads and Facebook Ads and shows spend next to leads. It doesn't join that to contact-level source data or cover organic, referral and offline, so blended cost per customer still gets built outside.

Tags or custom fields for source?

Both. Custom fields hold the exact campaign value for reporting, tags give you fast filtering and something to trigger workflows from.

Why are some contacts missing a source?

Imports and manual entries never had one, external forms may be missing the tracking code, untagged links carry no UTM values, and some API integrations don't set a source.