# How names connect to UTMs

Who owns what, as stated in utm-convention-and-registry's step 2 contract (checked 2026-10-17): when a team uses this skill, this skill owns the paid templates for `utm_campaign`, `utm_id` and `utm_content`. utm-convention-and-registry owns source, medium, the vocabulary, non-paid values, the UTM registry and the platform campaign map, and checks these templates against its channel-prediction and cost-join steps. This file sets the paid templates and how to prove they resolve.

## The paid templates

- `utm_id`: the ID of the initiative-level object, which is the object cost is reported on, by macro. Google Ads `{campaignid}`, Meta `{{campaign.id}}`, LinkedIn `CAMPAIGN_GROUP_ID` (export LinkedIn cost by campaign group, or map campaigns to their group in staging). Never changes on rename.
- `utm_content`: the ad or creative ID by macro. Performance Max uses `{assetgroupid}`.
- `utm_term`: the paid search keyword (`{keyword}`); on Meta the ad set ID (`{{adset.id}}`) only when ad set cost is joined; otherwise empty.
- `utm_campaign`, default route (cost joined in the warehouse, or imported to GA4 by file from the campaign map): the registry initiative name in the `yyyy_mm_initiative` format, for example `2026_10_pipeline_webinar`, at most 60 characters, typed statically. It is the same on every platform and owned channel of one initiative.
- `utm_campaign`, on a platform whose cost reaches GA4 through an automated connector (Meta, Pinterest, Reddit, Snap, TikTok): utm-convention-and-registry's cost step says the value must be the platform name. Use the name macro (`{{campaign.name}}` on Meta) and the six-token platform name, name the campaign before its first publish, and do not rename it while the connector runs. Confirm after the first sync that imported cost and sessions show the same campaign name.

Why the default keeps the platform name out of `utm_campaign`:

- One initiative has several platform names. The webinar in the worked example runs as `uk_leads_tofu_mm_pipeline-webinar_202610` on Meta and `uk_leads_mofu_ent_pipeline-webinar_202610` on LinkedIn. As `utm_campaign` they would split one webinar into two campaign rows in GA4.
- Name macros do not follow renames reliably. Meta fills name macros from a snapshot stored when the object carrying the tags is first published. LinkedIn does not document whether name values follow a rename. A Google Ads custom parameter is a typed copy.
- On a connector route GA4 shows the platform name per platform instead. The warehouse still groups the initiative through `utm_id` and the registry, so cross-channel reports are unaffected.

## The registry is the bridge

- Each initiative-level registry row holds the platform ID, the generated platform name, `initiative_id` and the `utm_campaign` its links send (the initiative name, or the platform name on a connector route).
- Fill utm-convention-and-registry's platform campaign map from these rows: `platform_campaign_id` from `object_id`, `platform_campaign_name` from `name`, plus `initiative_id` and `utm_campaign`. Cost joins use that map, never macro text.
- Weekly check: on the default route each `initiative_id` has one `utm_campaign`, equal to the UTM registry.

## Google Ads

- Keep auto-tagging on. A linked GA4 property receives the campaign and ad group names and IDs; the warehouse joins on `session_traffic_source_last_click.google_ads_campaign.campaign_id`.
- Manual UTMs serve tools that cannot read Google Ads data, such as CRM form capture; GA4 itself keeps the auto-tagged values. When they are needed, put the complete set in the account final URL suffix, as utm-convention-and-registry describes, with `utm_campaign` from a campaign-level custom parameter named `campaign` holding the registry initiative name:

```text
utm_source=google&utm_medium=cpc&utm_campaign={_campaign}&utm_id={campaignid}&utm_content={creative}&utm_term={keyword}
```

- Performance Max and Demand Gen campaigns need a campaign-level suffix, because some of these parameters do not populate there; Performance Max uses `utm_content={assetgroupid}` and no `utm_term`. Check the ValueTrack page for each campaign type.
- ValueTrack has no campaign-name or ad-group-name parameter. Use `{campaignid}`, `{adgroupid}`, `{creative}`, or `{assetgroupid}` for Performance Max. Other parameters insert text or codes, such as `{keyword}` and `{matchtype}`.
- Custom parameter limits disagree between Google's pages (checked 2026-10-17). The Google Ads Help page allows values up to 250 characters with any characters. The Ads Scripts reference allows 200 bytes without whitespace. Treat 200 bytes and no whitespace as the limit; a registry name of 60 characters fits both.
- The Google Ads API's `CustomParameter` definition lists use in `tracking_url_template`, `final_urls` and `mobile_final_urls`, not `final_url_suffix`, and no Google page found confirms that `{_campaign}` resolves in a Final URL suffix. Before rollout, click a test ad and read the landing URL. If it contains `{_`, move the parameters to the tracking template (`{lpurl}?utm_source=google&...`) and test again.

## Meta

- `url_tags` is a field of the ad creative. Ads Manager shows it as the ad's URL parameters. Source and medium come from utm-convention-and-registry. Default route:

```text
utm_source=facebook&utm_medium=paid_social&utm_campaign=2026_10_pipeline_webinar&utm_id={{campaign.id}}&utm_content={{ad.id}}
```

- GA4 Meta connector route: the same string with `utm_campaign={{campaign.name}}`.
- Name macros resolve from a snapshot stored when each ad or creative carrying the tags is first published. Meta's guide says nothing explicit about renames. After a campaign rename, ads published earlier and ads published later can send different names for one campaign ID. This is why the default types `utm_campaign`, and why the connector route forbids renames.
- Read `url_tags` of duplicated and newly added ads with `ads_get_creatives`, read-only. A missing or edited field on one creative is invisible in the campaign view.

## LinkedIn

Set tracking parameters on each campaign:

```text
dynamic:  utm_id       -> CAMPAIGN_GROUP_ID
          utm_content  -> CREATIVE_ID
custom:   utm_source   =  linkedin
          utm_medium   =  paid_social
          utm_campaign =  2026_10_pipeline_webinar
```

- `utm_id` is the campaign group because the group is the initiative level here; utm-convention-and-registry's LinkedIn recipe then puts group IDs in the campaign map and exports cost by campaign group.
- GA4 has no automated LinkedIn connector, so LinkedIn always takes the default route.
- Keep the set on each campaign. Ad account-level parameters exist from version `202608`, but they would give every campaign in the account the same static `utm_campaign`. The page (checked 2026-10-17) also does not say which level wins, or whether keys merge, when the account and a campaign both set parameters. Test on a draft campaign before setting both.
- Conversation Ads and Message Ads do not support tracking parameters. Tag their links statically from the registry.
- Remove static UTM keys on creatives that duplicate campaign keys. LinkedIn appends both.

## Prove the URLs resolve

Before launch, for one ad per platform:

- Open a preview or click the live ad and copy the landing URL after redirects.
- Meta and LinkedIn pass: no literal macro text (`{{`, `%7B%7B`, `{_`, an uppercase value such as `CAMPAIGN_GROUP_ID`), `utm_id` equals the initiative-level ID shown in the UI, `utm_campaign` equals the registry row's value (the initiative name, or the platform name on a connector route), no personal data in the URL.
- Google Ads pass: `gclid` present after redirects, and when manual parameters are used, `utm_campaign` equals the registry initiative name with no `{_` left.

Two to three days after launch, in the GA4 BigQuery export. This is an event-level check, so it reads `collected_traffic_source`, which holds the values present on each collected event:

```sql
SELECT
  collected_traffic_source.manual_source AS utm_source,
  collected_traffic_source.manual_campaign_id AS utm_id,
  collected_traffic_source.manual_campaign_name AS utm_campaign,
  COUNT(*) AS events
FROM `your_project.analytics_XXXXXXXXX.events_*`
WHERE _TABLE_SUFFIX BETWEEN '20261001' AND '20261007'
  AND collected_traffic_source.manual_medium = 'paid_social'
GROUP BY 1, 2, 3
ORDER BY events DESC
```

- Pass: every `utm_id` matches a directory row (a Meta campaign or a LinkedIn campaign group), no value contains macro text, and each `utm_id` shows exactly one `utm_campaign`, equal to its registry row (on a connector route, a second name appears only across a logged rename).
- Session reporting uses `session_traffic_source_last_click` instead; see `warehouse-parsing.md`.
