HubSpot UTM Tracking: Capture, Attribution and Reporting
Last updated · published
HubSpot classifies where a contact came from. It does not keep the campaign values you sent.
Original Source tells you “Paid search”. It will not tell you that the click came from utm_campaign=2026-q3-demand-gen, and by month three that is exactly what paid media, RevOps and the CFO all want.
So the work is this: capture the raw values into properties you own, write them on every form path, and pick an attribution model that still exists in your account.
TL;DR
- Original Source is a channel classification with two drill-downs. What each drill-down holds depends on the channel, and none of them preserves the full UTM set.
- Build your own first-touch and last-touch properties. HubSpot has no write-once setting, so a workflow enforces it.
- Forms auto-populate a field when the query-string key matches the field’s internal property name. There is no “read from URL parameter” default-value option.
hubspotutkis the visitor token.__hstcholds session bookkeeping, not your UTM values.- HubSpot consolidated its position-based attribution models into Empirical, so guidance recommending U-shaped is out of date.
What Original Source Actually Holds
Original Source is set automatically by the tracking code the first time HubSpot identifies a contact, and it is sticky unless something downstream overwrites it.
It is a triple: the channel itself, plus Drill-Down 1 and Drill-Down 2. What those two hold varies by channel, which is the detail most guides get wrong:
- Paid search: Drill-Down 1 is the campaign name from
utm_campaign, Drill-Down 2 is the search term. - Paid social: Drill-Down 1 is the social network, Drill-Down 2 is the campaign name.
- Email marketing: Drill-Down 2 carries the HubSpot email name, which is the one channel where
utm_contentsurfaces.
The documented channel values are Organic search, Paid search, Email marketing, Organic social, Paid social, Referrals, AI Referrals, Other campaigns, Direct traffic and Offline sources. AI Referrals is the recent addition and the one worth checking on your own account, since it is HubSpot’s counterpart to the AI Assistant channel GA4 now ships.
What Original Source does not do: preserve utm_source, utm_medium and utm_term as strings you can report on, track how attribution changed over time, or survive a careless import that includes the column.
Treat it as the floor of your attribution stack.
Capture the Raw Values Yourself
Twelve custom contact properties: six first-touch, six last-touch. Some teams add timestamps for each.
| Property | Type | Behaviour |
|---|---|---|
first_touch_utm_source and five siblings | Single-line text | Written once, never overwritten |
last_touch_utm_source and five siblings | Single-line text | Overwritten on every submission |
Create them under contact properties, grouped together so they stay findable.
HubSpot has no “write once” setting, so first-touch stickiness is a workflow: trigger on form submission, branch on whether first_touch_utm_source is unknown, and set the six values only then. Last-touch needs no branch, because the last touch is whatever just happened.
Getting UTMs Into the Properties
This is where most implementations break, because the mechanism is not what it looks like.
HubSpot forms auto-populate a field from the query string when the parameter name matches the field’s internal property name. There is no default-value option that reads an arbitrary URL parameter into an arbitrarily named field. A hidden field called first_touch_utm_source will never fill itself from ?utm_source=.
Two ways to work with that.
Match the names. Create plain capture properties named utm_source, utm_medium, utm_campaign and so on, add them as hidden fields, and let the query string populate them. Then run a workflow that copies those values into your first-touch and last-touch properties with the stickiness rule applied.
Populate with script. Read the query string on page load and write the values into hidden fields yourself before submission. More control, more code to maintain.
Either way, the tracking code has to be installed on the page hosting the form. Without it there is no visitor token, no session continuity, and nothing but whatever sits in the URL at the moment the form loads.
For forms that post to the CRM API directly, pass the values in the properties object:
{
"properties": {
"email": "lead@example.com",
"last_touch_utm_source": "linkedin",
"last_touch_utm_medium": "paid-social",
"last_touch_utm_campaign": "2026-q3-demand-gen"
}
}
The API enforces no stickiness. Your code checks whether the first-touch value exists before writing, or you send last-touch only and let a workflow handle first-touch on the property-change trigger. Keep that logic in one place.
What the cookies actually do
hubspotutk is the visitor token that ties a browser to a HubSpot contact record. __hstc holds session bookkeeping: the domain, that token, first and last timestamps, and a session number.
Neither stores your UTM values. The source data lives server-side against the visitor token, which is why a contact can keep an Original Source from a visit weeks ago while your custom properties stay empty until a form writes them.
Attribution Models: Check Before You Recommend
HubSpot’s current attribution reporting offers First Touch, Last Touch, Linear, Time Decay and Empirical, and HubSpot states that Empirical replaces the U-shaped, W-shaped, J-shaped and inverse J-shaped models.
Be careful here: HubSpot’s own documentation is inconsistent, with older definition pages still describing the position-based family. Check the model selector in your own account before writing a recommendation into a playbook, and treat any guide recommending U-shaped, including the earlier version of this page, as dated.
Multi-touch revenue attribution requires Marketing Hub Enterprise. Professional gives you contact-create attribution and the standard source reports.
The model is chosen per report, so two reports can disagree while both being correct. Standardise on one or two for executive reporting and label every report with the model it used.
Where UTMs feed this: HubSpot weighs page views, form submissions, email interactions and CRM activity, each carrying the channel assignment from the contact’s history. Clean values group cleanly. Inconsistent values fragment revenue across combinations that do not exist.
The custom report builder is where you join deals to contacts and aggregate closed-won revenue by first_touch_utm_campaign. That is the report the CFO eventually asks for, and it will not match the standard attribution reports, because those use HubSpot’s channel enum rather than your raw values. Pick one as the number of record.
Moving It Into a Warehouse
The CRM API is the direct route: contacts and deals with the properties you name, plus associations for the deal-contact roster. Authenticate with a private app token and the relevant read scopes. For large accounts, the search endpoint with cursor pagination beats per-record fetches.
HubSpot’s warehouse sync covers Snowflake and BigQuery as documented destinations, and it sits on the Enterprise tier rather than being available from Starter. Check the cadence in-account rather than trusting a number from an article.
Reverse ETL tools such as Hightouch and Census treat HubSpot as a source, handle the pagination and rate limits, and make sense when the warehouse is your source of truth or you need sync in both directions.
Once the data lands, first-touch attribution is a join from contacts to deal associations to deals, filtered to closed-won and grouped by your first-touch properties. Last-touch swaps the prefix. Anything multi-touch needs the interactions themselves, which the engagements endpoints provide.
Four Things That Go Wrong
Original Source gets overwritten. An import including the column writes it directly. The Salesforce connector can push values back when Lead Source is mapped across. Contact merges resolve to whichever value the merge logic picks. If Original Source is load-bearing for you, copy it into a custom property the first time it populates and report on the copy, because the native property cannot be made immutable.
Lead Source is not a HubSpot property. It is a Salesforce standard field. Teams who want one in HubSpot create a custom dropdown with controlled values and populate it with a workflow from first_touch_utm_medium. That mapping stays maintainable only if the medium values are constrained: three campaigns sending cpc, ppc and paidsearch means three branches somebody has to remember.
Reports disagree because models differ. One widget defaults to one model, an analyst’s report uses another, and both get called marketing-sourced revenue.
Consent behaviour is misread. HubSpot’s banner is available on every tier, but the default notification style does not block tracking: cookies load before consent. Blocking requires configuring opt-in, by category where you need it. If the visitor declines under an opt-in configuration, there is no visitor token and no cross-page persistence, though UTM values still reach a form that reads them from the URL directly.
Govern the Values Upstream
A HubSpot account running paid acquisition for two years accumulates a dozen spellings of the same source. The attribution report fragments, workflow branches multiply, and RevOps cleans it up once a quarter until it drifts again.
HubSpot is not the problem. It stores what you send.
Terminus, the marketing taxonomy governance platform, constrains the values when the link is built, so the properties above only ever receive the vocabulary you agreed on. That keeps your Lead Source workflow to one branch per channel instead of one branch per spelling.
Constrain the vocabulary before the link is published. HubSpot will faithfully store whatever you hand it.
FAQ
Does HubSpot capture UTM parameters automatically?
Partly. The tracking code reads them to classify Original Source and its drill-downs. The raw values are not kept on the contact unless you create properties and write them yourself.
What is the difference between Original Source and Latest Source?
Original Source is the channel from the first identified visit and is sticky. Latest Source reflects the most recent tracked session and overwrites itself. Both use HubSpot’s channel classification rather than your raw values.
Can Original Source change after it is set?
Yes. Imports that include the column, the Salesforce connector when Lead Source is mapped, and contact merges can all change it. Copy it to a custom property on first population if your reporting depends on it.
Why won’t my hidden field fill from the URL?
Because auto-population matches the query-string key to the field’s internal property name. A field named first_touch_utm_source does not fill from utm_source. Capture into matching property names first, then copy into your first-touch fields with a workflow.
Does __hstc store my UTM values?
No. It holds session bookkeeping. hubspotutk is the visitor token, and the source data sits server-side against it.
Which attribution models does HubSpot offer?
First Touch, Last Touch, Linear, Time Decay and Empirical, with Empirical replacing the position-based family. HubSpot’s own documentation is inconsistent on this, so check the selector in your account.
Does HubSpot have a Lead Source property?
No. That is a Salesforce field. Create a custom property with controlled values and populate it with a workflow if you want the equivalent.
How do I get attribution data into a warehouse?
The CRM API with a private app, HubSpot’s Enterprise-tier warehouse sync to Snowflake or BigQuery, or a reverse ETL tool that handles pagination for you.
Does the consent banner block tracking by default?
No. The default notification style loads cookies before consent. Blocking requires an opt-in configuration.
Can I join HubSpot deals to paid spend?
Yes, with utm_id. Pass a stable campaign ID on every paid link, capture it into a property, and join on it in the warehouse. HubSpot’s own campaign IDs work too when the spend data lives inside HubSpot.
Every account starts with a 21-day trial, no credit card required.