martechdevs has been acquired by Growth and Analytics Partners LLC

Tracking plans and naming

UTM convention and registry

Designs a UTM plan, a campaign tagging convention and registry so every tagged link lands in the GA4 default channel predicted before launch and its spend joins to sessions. Covers lowercase format rules, a source and medium vocabulary checked against GA4's live channel rules, utm_campaign and utm_id policy, a platform campaign map for cost joins, recipes for Google Ads, Microsoft Advertising, Meta, LinkedIn, TikTok, email, affiliates, AI assistant links and QR codes, a link builder and launch tests through to the CRM.

FreeVersion 0.3.0Reviewed 17 October 2026

Produces format rules and a controlled vocabulary for campaign parameters, a registry with one row per distributed link plus a map of real platform campaign IDs, and links tested from the real click to the GA4 channel, the CRM record and the cost row. A consistent pair can consistently land in the wrong channel. The convention passes when every link lands in the channel predicted before launch and its spend joins to its sessions.

When to use

Use this skill when:

  • Someone asks for a UTM plan, template, spreadsheet, naming convention or link builder.
  • Paid traffic shows as Organic Social, Paid Other, Referral or Unassigned and the collected source/medium pairs are the suspect.
  • A paid platform's cost has to join to sessions, in GA4 campaign data import or in a warehouse.
  • A new channel needs rules: an ad platform, a newsletter, an affiliate program, links inside an AI assistant, QR codes on print.

Use something else when:

  • Unassigned or (not set) appears and the tags may be fine (redirects, consent, tag order, server events): ga4-unassigned-not-set-diagnosis.
  • A launch test loses the campaign when consent changes: consent-debugging.
  • In-platform campaign, ad group and ad names: campaign-naming-convention. The contract between the two skills is in step 2.
  • Internal banners and navigation: events in a tracking plan (tracking-plan-builder), never UTMs.
  • Planned, not yet published: utm-link-audit, utm-governance-and-adoption, first-party-click-id-capture, ai-traffic-tracking, marketing-warehouse-design. If one is not installed, do the minimum here (step 1 inventory, an owner line in naming-rules.md, the step 7 CRM test, AI referrals left untagged, the step 8 join keys) and tell the user the rest is out of scope.

Inputs

Read these first if they exist:

  • martech-context/stack.md: ad platforms, ESP, affiliate network, redirect service, CRM, warehouse, GA4 property.
  • martech-context/naming-rules.md: current UTM and in-platform naming rules.
  • martech-context/decisions.md: earlier value choices and cutover dates.
  • martech-context/data-dictionary.md: cost tables and the CRM fields that store UTMs.
  • martech-context/tracking-plan/: an existing UTM or campaign registry. Extend it; never start a second one.

Then gather, read-only:

  • 90 days of GA4 data by Session source / medium, Session manual source, medium, campaign name, campaign ID and source platform, and Session default channel group, with sessions and key events (Google's GA4 MCP server, run_report). On auto-tagged Google Ads clicks only manual ad content and manual term come from UTMs. If an (other) row appears or the response flags dataLossFromOtherRow, split the request by month or read collected_traffic_source in the BigQuery export.
  • URL settings at every level on each ad platform, and LinkedIn parameters at account and campaign level. How to read each: references/channel-rules.md.
  • ESP tagging settings, the affiliate network's link format, how shorteners and QR redirects treat query strings, and how the CRM stores UTMs (first touch, latest touch).
  • The cost route per paid platform: GA4 product link, automated connector, file import, warehouse only, or none.

Never ask for credentials, tokens or customer records. Strip personal data from any URL sample before storing it.

Procedure

1. Inventory what is collected

  • Group by lowercased manual source and medium. Values that differ only by case are one value split across rows and a broken cost join: GA4 values and import matching are case-sensitive, channel rules are not.
  • Review every pair from a paid platform and every pair above 0.5% of sessions or with any key event. List the rest in bulk.
  • One line per reviewed pair: observed pair, sessions, key events, current channel, intended channel, action (keep, map, retire), cutover date.
  • Flag literal or encoded macros ({, }, %7B, CAMPAIGN_ID). Values used by a connector, CRM workflow or dashboard filter change only with a mapping and a cutover date in decisions.md.

2. Set the format rules

Each parameter does one job. Columns and ID policy: references/registry-and-cost-joins.md.

  • utm_source: who sent the visit (platform, publication, partner, placement). One slug per relationship; never a generic partner.
  • utm_medium: how it was distributed, from the list in step 3.
  • utm_campaign, non-paid: yyyy_mm_initiative, plus a qualifier only when it changes a decision, 60 characters at most. Always-on programs keep the month they started (2024_03_onboarding_flow).
  • utm_campaign, paid platforms: the platform's campaign name by its name macro, because Google's import and campaign URL pages ask for names that match the platform. Name platform campaigns with the registry name plus a qualifier (2026_11_forecasting_launch_prospecting), or by campaign-naming-convention.
  • utm_id: paid, the ID of the object cost is reported on, by macro. Everything else, a registry ID (cmp_2026_311), one per initiative per publication, partner or placement. Never reused, never changed on rename.
  • utm_content: the ad ID by macro, or a slot from the pair's list (hero_button). utm_term: the paid search keyword. utm_source_platform: the ESP, network or DSP when it is not the source.
  • utm_creative_format, utm_marketing_tactic: only when a named model or report reads them and a test visit shows the property returns them.
  • Lowercase a-z, 0-9 and _; mediums keep GA4's spelling (ai-assistant); macros stay as the platform expects. No emails, names, phone numbers or CRM IDs.

Contract with campaign-naming-convention: it owns in-platform names and, when the team uses it, the paid templates for utm_campaign, utm_id and utm_content. This skill owns source, medium, the vocabulary, non-paid values, the registry and the campaign map, and checks every paid template against steps 4 and 8. Reuse the initiative IDs that skill minted, whatever their prefix, and never mint a utm_id equal to one.

3. Build the controlled vocabulary

Copy assets/utm-vocabulary.yaml and assets/allowed-pairs.csv (one row per approved pair with expected channel, content slots and test result). Defaults checked on 2026-10-17:

  • cpc with a search source (google, bing): Paid Search. paid_social with a social source: Paid Social. paid_video with a video source: Paid Video.
  • display: Display. email: Email. sms: SMS. push: Mobile Push Notifications. audio: Audio. social: Organic Social. referral: Referral. affiliate: Affiliates. ai-assistant: AI Assistant.
  • offline (QR, print, events): no default rule, so Unassigned in the default group and Offline in a custom group.

Adding values:

  • A new medium needs an intended default channel, or a custom channel rule shipped with it.
  • A source with a paid medium must be on Google's social, search, video or shopping list, or it lands in Paid Other. The 2023 source file has facebook, fb, instagram, ig, linkedin, lnkd.in, not li, meta, msg, an or x; those are predicted Paid Other and confirmed with a test visit.
  • A listed source with a non-paid medium that has its own rule (linkedin / email, youtube / referral, facebook / affiliate) satisfies rules for two channels with no documented winner. Use the publication or partner as source.

4. Predict each pair's channel before launch

Walk every pair through references/ga4-channel-rules.md:

  • A listed source with a paid medium: that list's paid channel. A paid medium with no listed source and no other medium rule: Paid Other.
  • Rules pointing to two channels: test required, no prediction. Change a value so one channel's rules match. If nothing can change, run one controlled test visit before launch and store the observed channel in test_result; the builder refuses the pair until then.

These look sensible and misclassify:

  • A social source with social_paid, sponsored, boost or social (LinkedIn's own API sample): Organic Social, so paid spend reads as organic.
  • Mediums containing video, mobile or notification, or ending in push, match Organic Video or Mobile Push; when also paid (cpm, paid_video with an unlisted source), test required.
  • newsletter, partner, qr, print, ai, ai_assistant with an unlisted source: Unassigned. affiliates is untested and may be too.
  • Campaign names with shop or cross-network can trigger the Shopping or Cross-network rules.

5. Apply the channel rules

Recipes and read-only checks: references/channel-rules.md. The decisions that go wrong most:

  • Google Ads: keep auto-tagging on. Add manual UTMs only when a CRM or form tool needs them and cannot store click IDs and gad_campaignid. Then use the full set in the final URL suffix, a campaign custom parameter equal to each campaign's name, and record the fallback: clicks whose click ID cannot be used are classified from the UTMs alone, so google / cpc is Paid Search for every campaign type. Redirects keep gclid, gbraid, wbraid and gad_*.
  • Microsoft Advertising: UTM auto-tagging off and the full set in the account final URL suffix with {Campaign} and {CampaignId}. Never combine UTM auto-tagging with registry UTMs in the suffix.
  • Meta: {{campaign.name}} and {{campaign.id}} on every ad. Name macros keep the first-published name, so name campaigns before publishing and do not rename them on a connector route.
  • LinkedIn: parameters at one level only, dynamic name and ID, no UTMs on the creative URL.
  • AI assistant links you control: your own app slug as source, never an assistant's name. QR: print a 302 or 307 redirect on your own domain, never the tagged URL.
  • Never tag internal links or URLs that get indexed or reshared: later direct sessions inherit the latest campaign values.

6. Fill the registry and the campaign map

Copy assets/utm-registry-template.csv: one row per distributed link (columns).

  • Macro rows store the parameter string as entered and where (UI or API); static rows store the built URL. expected_channel comes from step 4 before approval.
  • At launch, add one row per platform campaign to assets/platform-campaign-map-template.csv from a read-only export: ID, name, the utm_campaign the links send, initiative_id, link_id, dates seen (columns). Cost joins use this map, never the macro text.
  • A rename keeps utm_id, adds a dated map row and a decisions.md entry. Ended rows stay.

7. Build and test the links

Run scripts/build_links.py as described in references/link-builder.md. It checks static rows against allowed-pairs.csv, the format patterns and personal data, keeps existing parameters and the fragment, and fails URLs over 1,000 encoded characters.

Test the resolved link. The agent writes a test sheet per row and reads GA4 the next day; a person clicks, preferring ad previews over live ads. Fresh private window, a network outside the internal traffic filter, no debug mode, a click from the real context.

  • Consent accepted on the landing page: the final URL and first GA4 request carry each utm_ key once, no literal macro. Next day the channel equals the prediction.
  • Consent rejected: check the request only (none in basic mode, cookieless in advanced). Record it; no next-day check.
  • Consent accepted on a second page: record whether the campaign survives; if not, consent-debugging and a decisions.md entry.
  • Conversion: the CRM record holds source, medium, campaign and utm_id from the URL, first touch kept, latest touch updated.

8. Line up the cost joins

Details: references/registry-and-cost-joins.md.

  • GA4 campaign data import joins on utm_source, utm_medium and date; utm_id and utm_campaign are recommended and must match logged values exactly. utm_content and utm_term cannot be imported. Cost needs a currency. Template: assets/cost-import-template.csv.
  • File imports: join the platform export to the map on platform and campaign ID, taking the utm_campaign live on each date. An ID missing from the map is a tagging gap: list it, never guess.
  • Automated connectors (Meta, Pinterest, Reddit, Snap, TikTok): one fixed source and medium per platform, unchangeable on Meta; utm_campaign must be the platform name, or use a file import from the map.
  • Warehouse: cost joins through the map to collected_traffic_source.manual_campaign_id and the CRM's utm_id; Google Ads on session_traffic_source_last_click.google_ads_campaign.campaign_id; affiliate commission to orders on the network's order ID.

9. Propose the files

Show the user and wait for approval before writing:

  • the UTM section of martech-context/naming-rules.md, with its owner
  • martech-context/tracking-plan/utm-registry.csv, utm-allowed-pairs.csv and platform-campaign-map.csv
  • decisions.md entries: cutovers, mappings, custom channel order, the Meta source and Google Ads manual UTM choices
  • stack.md additions: cost routes, ESP and redirect behaviour, consent mode type

Common failure modes

Detection queries for each: references/failure-modes.md.

  • Paid in Organic Social: Organic Social sessions whose manual campaign ID is in the map.
  • Literal or encoded macros: campaign values containing {, }, %7B or a macro name.
  • Case splits: values equal after lowercasing; low import match rate.
  • Meta name drift: one manual campaign ID with two names after a rename.
  • Duplicate keys: LinkedIn creative UTMs or both LinkedIn levels, auto-tagging next to registry values, Google syntax imported into Microsoft.
  • Stripped parameters: GA4's missing URL parameter diagnostic; shorteners, bio tools, redirects or single-page apps dropping the query.

Worked example

Acme Analytics, a fictional B2B SaaS, launches 2026_11_forecasting_launch. Full walk with inventory lines, an ambiguous pair and the map-to-import join: references/worked-example.md. Files: assets/example-acme-registry.csv, assets/example-acme-campaign-map.csv.

  • LinkedIn: the agency's li / paid (predicted Paid Other) became linkedin / paid_social with CAMPAIGN_NAME and CAMPAIGN_ID.
  • Newsletter: draft example_esp / newsletter (Unassigned) became acme_weekly / email; one issue carries two initiatives and two registry IDs.
  • linkedin / email for a LinkedIn newsletter was ambiguous, so it became linkedin / social.
  • Google Ads keeps auto-tagging plus a manual set for the CRM; Microsoft uses the suffix with auto-tagging off.

Acceptance checks

  • Vocabulary: every allowed pair has an expected channel or test required, with URL and date. Pass: no Paid Other or Unassigned without a reason or custom rule; every ambiguous pair in use has a pass result.
  • Format: static values ^[a-z0-9_]+$, mediums ^[a-z0-9_-]+$, campaigns ^20[0-9]{2}_(0[1-9]|1[0-2])_[a-z0-9_]+$; map utm_campaign values pass the house naming rules. Pass: zero violations.
  • IDs: every paid campaign with spend has a map row. Pass: on any date each platform campaign ID and each cmp_ ID maps to one utm_campaign; in GA4 each Session manual campaign ID has one name except across a logged rename.
  • Tests: every live row records the final URL, the consent-accepted first request and the observed channel. Pass: each utm_ key once, observed equals expected_channel; the consent-rejected run is recorded, not scored.
  • CRM: one test conversion per channel. Pass: fields equal the URL values, first touch unchanged by a later test visit, latest touch updated.
  • Platform settings: Microsoft's URL test tool shows each key once with utm_source=bing, rerun after Google imports; LinkedIn uses one level; every Google Ads campaign with manual UTMs has a campaign parameter equal to its name. Pass: no duplicate, missing or literal value.
  • Cost: every paid platform has a route. Pass: "% imported" is 100%; test-date cost equals the platform export within 1% in the same currency; names and IDs in the Non Google campaign report match the map; unmatched rows are fixed or explained.
  • Privacy: no static value contains @, 7 or more digits in a row, or a CRM ID; no internal URL is in the registry. Pass: the builder and a search return none.
  • Approval: nothing in martech-context/ changed without the user's approval.

Boundaries

  • This skill writes a convention, a registry, a campaign map, a test sheet and draft URLs. It does not change ad platform, GA4, ESP, CRM or redirect settings or published links; each change is a draft applied by a human.
  • A custom channel group needs a GA4 Editor. New values do not rewrite GA4 history; record the cutover and give analysts the mapping.
  • Hand-offs:
    • GA4: Google's GA4 MCP server, read-only.
    • Google Ads: Google's Google Ads MCP server, strictly read-only (GAQL). Edits in the UI or Editor after approval.
    • Microsoft Advertising: Microsoft's Advertising MCP server, read-only through open flight (August 2026 product news), for account and campaign reads. Confirm live whether it returns tracking templates and suffixes; otherwise an account user reads URL options and runs the URL test tool.
    • Meta: Meta's Ads MCP server, read tools only; URL parameter changes go through a human in Ads Manager.
    • LinkedIn: no official MCP server; an account admin edits tracking parameters in Campaign Manager or the Marketing API.
    • consent-debugging for consent-state failures; ga4-unassigned-not-set-diagnosis for missing rows after a correct link.

Live docs

Platform settings, limits and menu paths change. Before stating any parameter name, limit, quota or UI path:

  • Fetch the current page from the vendor docs MCP server or llms.txt listed in sources.yaml, or open the canonical URL.
  • If the live page disagrees with this skill, follow the live page and tell the user what changed.
  • Cite the URL you used.

The entries that matter most: Google's default channel group page and source spreadsheet, combining tagging methods, aggregate identifiers, campaign data import, the ValueTrack page, Microsoft's UTM auto-tagging, final URL suffix and URL parameters pages, Meta's URL tags guide and the GA4 Meta connector page, and LinkedIn's Dynamic UTM Tracking page. Platform facts were checked on 2026-10-17.

Book a Free Call

Thirty minutes on your stack and what it needs. Free, and no pitch.

Thirty minutes, and you leave with a plan

We look at what you have, what it is costing you and what to do about it first.

  • 30 minutes
  • Video call, link on booking
  • No obligation

What we'll cover

  • Your current stack and where it hurts
  • Quick wins we can ship first
  • An integration roadmap
  • Timeline, scope and next steps

Calendar not loading? Open it in a new tab or email hello@martechdevs.com.