The UTM taxonomy that survives a marketing team
Every team has a UTM convention document. Almost none have clean UTM data. The gap is enforcement — here is a taxonomy small enough to follow and a way to check it.
lowercase · hyphens · closed list for medium · never empty · validate before launch
Analytics groups on the exact string, so Facebook and facebook are two channels. The fix is not a longer document — it is a shorter vocabulary plus a check that runs before a campaign goes live.
Keep utm_medium to six or seven values, forever. That is the parameter you group by when someone asks how much revenue comes from paid.
Your UTM convention document is not the problem. Every team has one, and almost every team has dirty UTM data anyway.
The problem is that a convention checked by memory, at 6pm, by someone launching a campaign in a hurry, is not a convention.
What drift costs
Three people launch in the same week:
?utm_source=facebook&utm_medium=cpc&utm_campaign=spring-sale
?utm_source=Facebook&utm_medium=paid-social&utm_campaign=Spring Sale
?utm_source=fb&utm_medium=ppc&utm_campaign=spring-sale
One campaign. Three rows. Each with a third of the conversions and a CPA that looks three times worse than reality.
Nobody gets an error. The report just becomes wrong in a way that looks like underperformance — so the reasonable reaction is to cut spend on something that was working.
The taxonomy
Short enough that people can actually hold it.
| Parameter | Rule | Good | Bad |
|---|---|---|---|
utm_source |
The platform. One canonical value each. | facebook |
FB, Facebook, facebook.com |
utm_medium |
Closed list. How it was delivered. | cpc |
paid-social, PPC, paid_search |
utm_campaign |
The initiative. Lowercase, hyphenated, dated. | spring-sale-2026 |
Spring Sale, spring sale |
utm_content |
Creative or placement variant. | video-a |
Video A (final v2) |
utm_term |
Keyword — search only. | running-shoes |
Running Shoes |
utm_id |
Stable platform campaign id, where available. | 23851234567 |
(empty) |
The closed list for medium
This is the rule that matters most, and it is the one always broken first.
cpc paid search and paid social clicks
display banner and programmatic
social organic social
email every email, including transactional
affiliate partners and affiliates
referral genuine referrals
Six values. It should never grow. Source can be long-tailed — you will always add platforms — but medium is the axis you aggregate on when someone asks “how much of our revenue is paid?” The moment there are eleven mediums, three of which mean paid social, that question stops having an answer.
Campaign naming
Pick a pattern and use it everywhere:
{initiative}-{audience}-{yyyymm}
spring-sale-prospecting-202604
retarget-cart-abandon-202604
launch-lamp-broad-202605
Three benefits: it sorts chronologically, it is greppable, and the audience is visible in the report without opening the ad platform.
Rules that make it enforceable
1. Lowercase everything. No exceptions, no judgement calls.
2. Hyphens, never spaces or underscores. A space becomes %20 or + depending on who encoded it, which is two more variants of the same value.
3. Never leave a required parameter empty. utm_campaign= is worse than omitting it — some tools record an empty string as a distinct value.
4. One vocabulary, one place. Not a Notion page nobody opens. Somewhere the link builder reads from.
5. Build links, do not type them. The only reliable way to keep vocabulary consistent is to remove the step where a human types.
6. Validate before launch. Ten seconds against a checker beats a fortnight of a confusing report.
Fixing what you already have
You cannot rewrite history, and you do not need to. Normalise on read: map the variants to the canonical value at report time, and keep the original strings on the events.
facebook ← Facebook, FB, fb, facebook.com, meta
cpc ← CPC, ppc, PPC, paid, paid-social, paidsocial, paid_search
Nothing is lost, the grouping is fixed, and past traffic regroups the moment you declare the canonical value.
Where to start
Paste your last month of campaign URLs into the UTM validator. It shows every casing difference, every synonym, every missing parameter, and prints the vocabulary those URLs should have used.
Most teams find between three and eight distinct problems in the first pass, and every one of them is currently splitting a row in their report.
Frequently asked questions
What is the difference between source and medium?
Source is where the traffic came from (facebook, google, newsletter). Medium is how it got there (cpc, email, social, referral). The most common mistake is putting the platform in both, which makes medium useless for grouping.
How many mediums should I have?
Six or seven, and the list should never grow. Source can be long-tailed because you will always add platforms; medium is the axis you aggregate on, so it has to stay closed.
Should I use underscores or hyphens?
Hyphens. Underscores are harder to read in a report and some tools treat them inconsistently. Pick one and never mix — mixing is worse than either choice.
What about ad platform macros?
Use them for utm_content and utm_id where the platform substitutes a stable value. Keep source, medium and campaign hand-controlled, because macros are exactly where uncontrolled vocabulary enters.
Can I fix historical data?
You can normalise on read — map variants to a canonical value at report time. The underlying events keep their original strings, which means nothing is lost and the grouping is fixed.
Check the campaigns you already launched
Paste your last month of campaign URLs and see every casing difference, synonym and missing parameter splitting your reports right now.
Stop guessing which ad made the sale.
Flowsk Signals stitches the anonymous click to the email to the purchase — first-party, server-side, de-duplicated. One snippet, $29/mo, and every conversion comes with a receipt you can inspect.