• Created in public
  • Governed by the community
  • Owned by no vendor
Draftv0.1

UTM Standard

Deterministic rules for generating UTM parameters. Given the same campaign, source, and distribution context, two conforming agents generate the same UTM.

Same context + same standard = same UTM.

Overview

What it does

Teams and AI agents tag the same campaign differently: LinkedIn, linkedin.com, li. Reports fragment, and channel attribution in Google Analytics drifts.

The UTM Standard removes the guesswork. It defines which inputs are required, how each parameter is derived, and what to return when context is missing. Parameter meanings come from Google Analytics. Canonical values come from the open Source Registry.

  • Campaign

    Approved campaign identity

    utm_campaign
  • Source

    Resolved from the Source Registry

    utm_source
  • Distribution context

    organic, paid, owned, display

    utm_medium
Input
{
  "campaign": {
    "name": "Spring Product Launch"
  },
  "source": "linkedin",
  "distribution_context": "organic",
  "destination_url": "https://example.com/product"
}
Output — standard 0.1.0
{
  "status": "ok",
  "standard_version": "0.1.0",
  "parameters": {
    "utm_source": "linkedin",
    "utm_medium": "social",
    "utm_campaign": "spring_product_launch"
  },
  "url": "https://example.com/product?utm_source=linkedin&utm_medium=social&utm_campaign=spring_product_launch"
}

Standard

Parameters

The minimum conforming set is utm_source, utm_medium, utm_campaign. Every other parameter is set only from supplied inputs. Agents never invent values.

ParameterRequirementValue fromDerivation
utm_sourceRequiredSource Registry

The canonical_source of the resolved source record.

Google meaning: The referrer of the traffic, such as a search engine, newsletter, or other source.

Google-defined
utm_mediumRequiredSource + distribution context

contexts[distribution_context].utm_medium of the resolved source record.

Google meaning: The marketing medium, such as cpc, banner, or email newsletter.

Google-defined
utm_campaignRequiredApproved campaign identity

campaign.slug exactly if present; otherwise the campaign slug algorithm applied to campaign.name.

Google meaning: The name of the campaign, product, promotion, or other identifier.

Google-defined
utm_idRecommendedCampaign system

The campaign ID from the system of record, passed through unchanged.

Google meaning: The campaign ID used to identify a specific campaign or promotion.

Google-defined
utm_source_platformRecommended where applicablePlatform

The buying or management platform directing the traffic, when one exists. Normalized with the value format rule.

Google meaning: The platform responsible for directing traffic to a given property, such as a buying platform that sets budgets and targeting criteria.

Google-defined
utm_contentOptionalCreative / asset

The creative or asset identifier, normalized with the value format rule.

Google meaning: Used to differentiate ads or links that point to the same URL.

Google-defined
utm_termOptionalKeyword / targeting

The paid keyword or targeting term, normalized with the value format rule.

Google meaning: Identifies paid search keywords.

Google-defined
utm_creative_formatOptionalCreative format

The creative format, normalized with the value format rule.

Google meaning: The type of creative, such as display, native, video, or search.

Google-defined
utm_marketing_tacticOptionalMarketing tactic

The targeting criteria or tactic, normalized with the value format rule.

Google meaning: The targeting criteria applied to a campaign, such as remarketing or prospecting.

Google-defined

Standard

Campaign naming

utm_campaign represents campaign identity. It is not a container for every available marketing dimension.

Precedence

  1. If campaign.slug exists, use it exactly. Do not re-normalize it.
  2. Otherwise derive the slug from campaign.name with the slug algorithm.
  3. If neither exists, return requires_context with campaign in missing.

Slug algorithm

  1. Apply Unicode NFKD normalization and remove combining marks.
  2. Convert to lowercase.
  3. Replace every run of characters outside a-z and 0-9 with a single underscore.
  4. Trim leading and trailing underscores.

Result must match ^[a-z0-9]+(_[a-z0-9]+)*$ and stay stable for the life of the campaign.

Do not add unless part of campaign identity

  • source
  • medium
  • platform
  • placement
  • creative
  • audience
  • ad_set
  • keyword
  • date
  • year
  • quarter

Examples

  • Spring Product Launchbecomesspring_product_launch
  • Q3 Partner Summit — Berlinbecomesq3_partner_summit_berlin
  • Café Rewards Programbecomescafe_rewards_program

A supplied campaign.slug is always used exactly, even when the name differs.

Standard

Generation

Steps

  1. 1Resolve source to a registry record by exact match on canonical_source or aliases, case-insensitive.
  2. 2Resolve distribution_context against the record's contexts.
  3. 3Set utm_source and utm_medium from the record.
  4. 4Set utm_campaign from campaign naming precedence.
  5. 5Set recommended and optional parameters only from supplied inputs. Never invent values.
  6. 6Apply the organization profile, if any.
  7. 7Assemble the URL using url_assembly.

URL assembly

  • Remove any existing utm_* parameters from the destination URL.
  • Preserve all other existing query parameters in their original order.
  • Append UTM parameters after existing parameters in parameter_order.
  • Omit parameters that have no value. Never emit an empty parameter.
  • Percent-encode values per RFC 3986. Conforming values require no encoding.
  • Preserve the URL fragment after the query string.

When generation cannot complete

Missing context. Do not guess. Return a requires_context response listing every missing required input.

{
  "status": "requires_context",
  "missing": [
    "distribution_context"
  ]
}

Unknown source. Do not invent a source. Return an unknown_source response.

{
  "status": "unknown_source",
  "source": "<input>"
}

Unsupported context. Do not substitute a different context. Return an unsupported_context response listing supported contexts.

{
  "status": "unsupported_context",
  "source": "<canonical_source>",
  "supported": [
    "<context>"
  ]
}

Provenance

What Google defines, what SchemaFirst defines

Every value in the standard and registry is labeled with where it comes from. SchemaFirst is an independent community standard. It is not authored or endorsed by Google.

  • Google-defined

    Semantics, supported parameters, channel behavior, and documented recommendations from Google Analytics.

  • SchemaFirst default

    Canonical implementation decisions made by SchemaFirst for deterministic agent generation. Not authored or endorsed by Google.

  • Organization override permitted

    Defaults that may be replaced through an explicit organization profile.

Google Analytics references

Overrides

Organization profiles

Organization profile > SchemaFirst default. Google-defined parameter semantics cannot be overridden.

Permitted

  • Require recommended or optional parameters.
  • Define proprietary source records.
  • Define additional distribution contexts.
  • Override defaults marked as overridable.
  • Define where organization-owned values come from.

Not permitted

  • Redefine the meaning of Google-defined UTM parameters.
  • Remove a parameter from the minimum conforming set.
  • Change the campaign slug algorithm.
organization-profile.json
{
  "extends": "https://www.schemafirst.org/standards/utm/0.1",
  "organization": "Example Company",
  "overrides": {
    "utm_id": {
      "required": true
    }
  }
}

Community

Versioning and contributions

The standard uses semantic versioning. Changes to any of the following require a new major version:

  • parameter semantics
  • required fields
  • campaign naming
  • generation behavior
  • override precedence

The Source Registry is versioned independently. Adding a source record and Adding a distribution context to a source record are non-breaking.

Propose sources, contexts, and changes through GitHub. Each source record lives in its own file so changes are reviewed individually.

Licensed under Apache-2.0.