Metric Discrepancies & Attribution

UTM Governance: A Naming Convention That Survives Three Agencies

By Chinmay Raibagkar·August 28, 2026·10 min read·Some SQL

The 60-second version

Most UTM schemes die the first time someone else touches them. A convention, a validator query, and the four rules that keep a taxonomy intact across agency handovers.

  • What happened, in one line
  • What to do about it this week
  • What you can safely ignore

Open your channel report and count the rows. If you see facebook, Facebook, FB, fb, facebook.com, meta, and Facebook_Ads, you do not have a source problem — you have seven rows describing one channel, and every ROAS number computed against any single one of them is wrong.

This did not happen because anyone was careless. It happened because three agencies, two freelancers, one intern and an email platform each built links over four years, and nothing ever validated them. A UTM taxonomy is not a spreadsheet you write once. It is a convention plus an enforcement mechanism, and without the second half the first half decays within about two months of the person who wrote it leaving.


Why conventions decay

The Standard Decay Path

Process Flow
1

Someone writes the doc

A thorough Google Doc, correct in every particular, shared in a Slack channel that later gets archived.

2

It works for one quarter

While the author is personally reviewing links, compliance is near-perfect.

3

A new agency onboards

They have their own convention, from their own last client. Nobody sends them the doc. They ship 200 links.

4

Someone builds a link on a phone

Autocapitalisation turns `newsletter` into `Newsletter`. A new row appears in the report, permanently.

5

The report becomes untrustworthy

Someone notices the numbers are wrong, does a manual cleanup, and writes a new doc. Return to step 1.

The doc is not the failure. The failure is that nothing between the doc and the report ever checks. Every durable UTM system has a validator; every fragile one has only a convention.


The four rules

Everything else is detail. These four are what actually determine whether a taxonomy survives a handover.

Rule 1: source and medium are a closed set. Everything else is open.

This is the single most important rule and the one most often broken.

utm_source and utm_medium are the two fields your channel grouping depends on, so they must come from a fixed, short, enumerated list that only changes through a deliberate decision. utm_campaign, utm_content and utm_term can be free-form, because nothing structural depends on them.

FieldGovernanceValues
utm_sourceClosed set — additions require a decisiongoogle, meta, linkedin, newsletter, partner, podcast
utm_mediumClosed set — should almost never changecpc, paid_social, email, organic_social, affiliate, referral
utm_campaignOpen, but pattern-enforced2026q3_diwali_prospecting
utm_contentOpencarousel_v3_lifestyle
utm_termOpen, usually auto-populatedKeyword or audience

Agencies push back on closed sets because their internal naming differs. That is exactly the point: they conform to yours, or their data is unusable alongside everyone else's.

utm_medium is not a channel name. The most common taxonomy error is utm_medium=facebook — but medium is the type of traffic (paid_social), and source is where it came from (meta). Getting this backwards breaks GA4's default channel grouping, and GA4 will silently reclassify your traffic into "Unassigned" rather than telling you.

Rule 2: Lowercase, underscores, no spaces. Enforced, not requested.

UTM values are case-sensitive in GA4 and in almost every warehouse. Newsletter and newsletter are two different rows forever — there is no retroactive merge.

  • Lowercase everything. Autocapitalisation on mobile keyboards is the largest single source of case drift.
  • Underscores, never spaces. A space becomes %20 or + depending on the client, so one campaign name can produce three distinct values.
  • No punctuation beyond underscore and hyphen. Ampersands terminate parameters and silently truncate the rest of your URL.
  • Pick underscore or hyphen and never mix. Both work; using both means black_friday and black-friday coexist.

Rule 3: utm_campaign carries structure, in a fixed field order

A campaign name is the one place multiple facts have to coexist, so give it a grammar:

<period>_<initiative>_<funnel_stage>
2026q3_diwali_prospecting
2026q3_diwali_retargeting
2026q4_newyear_prospecting

Fixed field order matters because it makes the name parseable in SQL. With a grammar, SPLIT(utm_campaign, '_')[OFFSET(2)] gives you funnel stage as a dimension across every campaign ever run. Without one, funnel stage is locked inside a string and every analysis needs a hand-written CASE WHEN that goes stale.

Two campaign names, one parseable

Why grammar matters
UngovernedDiwali Sale - Retargeting (Copy)Spaces, capitals, parentheses, no field order. Unparseable.
Governed2026q3_diwali_retargetingThree fields, fixed order, safe characters.
Analysis unlockedProspecting vs. retargeting ROASOne SPLIT() across every campaign, no CASE WHEN maintenance.
Analysis unlockedQuarter-over-quarter by initiativePeriod is a field, so year-on-year comparison is a GROUP BY.
The governed version costs the same to type. The difference is entirely in what you can ask of it two years later, when nobody remembers what the campaigns were called.

Internal links carrying UTMs start a new session in GA4 and reassign the source, which means a customer who arrived from a paid ad and then clicked an internal banner is re-attributed to that banner. Your paid channel loses the conversion; a homepage carousel gains it.

Use a separate, non-UTM parameter for internal promo tracking — int_promo=homepage_hero, say — and either strip it in your tag configuration or simply do not use UTMs for it. This one mistake can distort a channel report by 20% or more and produces no error anywhere.


Enforcement: the part that makes it survive

The convention is the easy half. Here is what actually holds it together across handovers.

A builder, not a document

Give everyone a link builder that only emits valid values: dropdowns for source and medium, a pattern-validated field for campaign. Even a shared spreadsheet with data-validation dropdowns and a CONCATENATE formula beats a document, because it makes the correct thing the path of least resistance. A person who has to look something up will guess; a person given a dropdown will pick.

A validator query that runs on a schedule

This is what catches drift regardless of who created the link. Run it weekly and route the output somewhere a human reads:

UTM Drift Detector — Run Weekly

Show query

Catch drift at 40 sessions, not 40,000. A weekly validator surfaces a bad convention while it has affected one campaign. A quarterly review surfaces it after a full quarter of reporting has been built on it — at which point the only options are a painful backfill or a permanent asterisk in your historical data.

A normalisation view, because history is immutable

You cannot retroactively fix links that have already been clicked. What you can do is normalise at read time, so every downstream report sees clean values regardless of what was in the URL:

Normalisation View — Clean Values Without Rewriting History

Show query

Point every report at the view, never the raw table. New drift then costs one WHEN clause instead of a rewrite.

A handover checklist

The moment a taxonomy is most at risk is an agency transition. Make these four things part of the contract:

  1. The approved source and medium lists, as data, not prose.
  2. The campaign-name grammar with three worked examples.
  3. Read access to the validator output, so they see their own violations.
  4. An explicit statement that unapproved values will be normalised into whatever bucket the view assigns, which is usually not the one they wanted.

What good looks like after six months

Three Levels of UTM Maturity

Reporting Hierarchy
Tier 1
Documented

A convention exists in a doc. Compliance depends entirely on whether people read it. Decays within a quarter of the author leaving.

Convention only
Tier 2
Enforced at build

A link builder with dropdowns makes valid links the easy path. Catches most drift, but nothing stops a hand-typed link.

Convention + builder
Tier 3
Enforced at read

A weekly validator catches drift within days, and a normalisation view means historical drift never reaches a report. Survives handovers.

Convention + builder + validator + view

The third tier is roughly a day of work, and it is the only one that is still standing after three agencies.


Frequently asked questions

Are UTM parameters case-sensitive?

The parameter names are conventionally lowercase and treated case-insensitively by most tools, but the values are case-sensitive everywhere that matters — GA4, BigQuery, and every warehouse. Email and email are two permanent rows.

Generally no. Google Ads auto-tagging with gclid gives richer data than UTMs and does not conflict with GA4's channel grouping. Manual UTMs on Google Ads links can override auto-tagging and lose you the click-level detail. Use auto-tagging, and use UTMs for the channels that have no equivalent — email, partners, podcasts, influencers.

What about utm_id?

utm_id is intended to carry a campaign identifier that ties back to a cost-data import in GA4. It is genuinely useful if you are importing cost data, and pointless otherwise. It does not replace utm_campaign.

How do I fix historical data that is already messy?

You do not fix it — you normalise it at read time with a view, as above. Rewriting stored session data is possible in a warehouse and almost never worth it: the mapping logic has to exist either way, and putting it in a view means it stays visible and correctable.

Should the agency get to add new sources?

They should be able to request one, and someone on your side approves it. An open source list is functionally the same as no list — within two quarters you are back to seven rows for one channel.


The summary

  • Closed set for source and medium; open but pattern-enforced for campaign. This is the rule everything else hangs off.
  • Lowercase, underscores, no spaces, no mixing separators — enforced by a builder, not requested in a doc.
  • Give utm_campaign a fixed field grammar so it can be parsed in SQL rather than decoded by hand.
  • Never UTM-tag internal links; it restarts the session and steals the conversion from the channel that earned it.
  • Run a drift validator weekly and normalise at read time. The convention is not the system — the validator is the system.

Related: how channel CAC depends on this taxonomy being clean, and what to do when ad spend and orders share no join key.

Free tool

Blended CAC Calculator

Total spend across every channel, divided by total new customers — the acquisition cost number that reconciles with what you actually spent.

CR

Chinmay Raibagkar

About author →

Founder of DataLens AI. He helps non-technical teams read their ad and database numbers with confidence — which number to trust, what to do next, and what to ignore.