<!-- Cogny documentation. Canonical page: https://cogny.com/docs/utm-strategy -->

# UTM Parameter Strategy & Builder Reference

Complete guide to UTM parameter strategy, naming conventions, GA4 channel grouping rules, auto-tagging behavior, common mistakes, and BigQuery validation queries for marketing attribution.

**Author:** Cogny Team  
**Published:** 2025-02-11  
**Updated:** 2026-10-09  
**Canonical:** https://cogny.com/docs/utm-strategy

## Overview

UTM (Urchin Tracking Module) parameters are query-string tags appended to URLs that tell Google Analytics 4 where traffic originated. Correct UTM hygiene is the foundation of marketing attribution — get it wrong and your channel reports, ROAS calculations, and budget allocation decisions are built on bad data.

This guide covers the five UTM parameters, GA4's default channel grouping logic, naming conventions, auto-tagging behavior, common mistakes, and BigQuery queries to audit your UTM data.

## The Five UTM Parameters

| Parameter | Required | Purpose | Example |
|-----------|----------|---------|---------|
| `utm_source` | Yes | Identifies the platform or publisher sending traffic | `google`, `facebook`, `linkedin`, `newsletter` |
| `utm_medium` | Yes | Identifies the marketing channel type | `cpc`, `paid-social`, `email`, `organic-social` |
| `utm_campaign` | Yes | Identifies the specific campaign | `spring-sale-2025`, `brand-awareness-q1` |
| `utm_term` | No | Identifies paid search keywords | `running+shoes`, `crm+software` |
| `utm_content` | No | Differentiates creative variants or link placements | `hero-banner`, `sidebar-cta`, `blue-variant` |

### Parameter Details

**utm_source** — The platform or property sending the traffic. Think of it as "where did the click physically happen?"

```
utm_source=google          # Google Ads or Google organic
utm_source=facebook        # Meta Ads (Facebook/Instagram)
utm_source=linkedin        # LinkedIn Ads or organic
utm_source=klaviyo         # Email via Klaviyo
utm_source=tiktok          # TikTok Ads
utm_source=partner-blog    # A specific referral partner
```

**utm_medium** — The channel type. This is the most important parameter for GA4 default channel grouping (see below). If you get `utm_medium` wrong, your traffic lands in the wrong channel.

```
utm_medium=cpc             # Cost-per-click (paid search)
utm_medium=paid-social     # Paid social ads
utm_medium=email           # Email marketing
utm_medium=organic-social  # Organic social posts
utm_medium=display         # Display/banner ads
utm_medium=affiliate       # Affiliate traffic
utm_medium=referral        # Partner/referral traffic
utm_medium=video           # Video ads (YouTube, etc.)
```

**utm_campaign** — The campaign name. Use a consistent structure that encodes useful metadata.

```
utm_campaign=spring-sale-2025
utm_campaign=brand-awareness-q1-2025
utm_campaign=retargeting-cart-abandoners
utm_campaign=webinar-ai-marketing-apr
```

**utm_term** — The keyword or targeting term. Primarily used for paid search, but useful for any keyword-targeted campaign.

```
utm_term=running+shoes
utm_term=best+crm+software
utm_term=brand+name
```

**utm_content** — The creative variant, ad position, or CTA. Use this to A/B test different ads, links, or placements within the same campaign.

```
utm_content=hero-image-v1
utm_content=sidebar-cta
utm_content=email-header-link
utm_content=video-30s
```

### Full URL Example

```
https://example.com/landing-page?utm_source=facebook&utm_medium=paid-social&utm_campaign=spring-sale-2025&utm_content=carousel-ad-v2
```

## GA4 Source/Medium Mapping

### How GA4 Processes UTMs

When a user clicks a URL with UTM parameters, GA4:

1. Reads `utm_source`, `utm_medium`, and `utm_campaign` from the URL query string
2. Stores them as session-scoped dimensions (`sessionSource`, `sessionMedium`, `sessionCampaignName`)
3. Applies default channel grouping rules based primarily on `source` and `medium`
4. For first-visit attribution, stores values in the user-scoped `traffic_source` struct

### BigQuery Field Mapping

In the GA4 BigQuery export, UTM values appear in multiple locations:

| UTM Parameter | BigQuery Location | Scope |
|---------------|-------------------|-------|
| `utm_source` | `traffic_source.source` | User (first-touch) |
| `utm_medium` | `traffic_source.medium` | User (first-touch) |
| `utm_campaign` | `traffic_source.name` | User (first-touch) |
| `utm_source` | Event param `source` via `collected_traffic_source` | Session |
| `utm_medium` | Event param `medium` via `collected_traffic_source` | Session |
| `utm_campaign` | Event param `campaign` via `collected_traffic_source` | Session |

**Important:** `traffic_source` is user-scoped and reflects the user's first acquisition source. For session-level attribution, use `collected_traffic_source` fields or extract from `event_params` on `session_start` events.

```sql
-- Session-level source/medium from session_start events
SELECT
  (SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'source') as session_source,
  (SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'medium') as session_medium,
  (SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'campaign') as session_campaign,
  COUNT(*) as sessions
FROM `project.analytics_123456789.events_*`
WHERE _TABLE_SUFFIX BETWEEN FORMAT_DATE('%Y%m%d', DATE_SUB(CURRENT_DATE(), INTERVAL 30 DAY))
                        AND FORMAT_DATE('%Y%m%d', CURRENT_DATE() - 1)
  AND event_name = 'session_start'
GROUP BY 1, 2, 3
ORDER BY sessions DESC
```

## GA4 Default Channel Grouping Rules

GA4 classifies every session into a default channel group based on `source`, `medium`, and sometimes `campaign` values. These rules determine how traffic appears in GA4's standard reports.

### Channel Grouping Logic

| Channel | Rules (conditions are OR unless stated) |
|---------|-------|
| **Direct** | Source is `(direct)` AND medium is `(not set)` or `(none)` |
| **Organic Search** | Medium exactly matches `organic` |
| **Paid Search** | Medium matches regex `^(cpc\|ppc\|paidsearch)$` |
| **Display** | Medium matches regex `^(display\|cpm\|banner)$` |
| **Paid Social** | Medium matches regex `^(paid-social\|paidsocial\|paid_social)$` OR (medium is `cpc`/`ppc`/`paid` AND source matches a social platform) |
| **Organic Social** | Medium matches regex `^(organic-social\|social\|social-network\|social-media\|sm\|social_network\|social_media)$` OR source matches a known social platform with no paid medium |
| **Email** | Medium exactly matches `email` |
| **Affiliates** | Medium exactly matches `affiliate` |
| **Referral** | Medium exactly matches `referral` |
| **Audio** | Medium exactly matches `audio` |
| **SMS** | Medium matches regex `^(sms\|sms_message)$` |
| **Mobile Push Notifications** | Medium matches regex `^(push\|mobile\|notification)$` (must end with `push`, `mobile`, or `notification`) |
| **Video** | Medium matches regex `^(video\|youtube)$` |
| **Cross-network** | Campaign name contains `cross-network` |
| **Unassigned** | Does not match any rule above |

**Known social platform sources used by GA4:** `facebook`, `instagram`, `linkedin`, `twitter`, `t.co`, `pinterest`, `youtube`, `reddit`, `tiktok`, `snapchat`, `threads.net`, and others.

### Critical Takeaway

If `utm_medium` does not match any of the patterns above, your traffic falls into **Unassigned**. The most common cause is using non-standard medium values like `social` when you meant `paid-social`, or `ppc` for a social campaign.

## Naming Conventions

### Rules

1. **Lowercase everything.** GA4 is case-sensitive. `Facebook` and `facebook` create two separate source entries.
2. **Use hyphens as word separators.** Avoid spaces (they become `%20`), underscores are acceptable but hyphens are preferred for URL readability.
3. **No special characters.** Stick to `a-z`, `0-9`, and `-`. Avoid `&`, `=`, `?`, `#`, `+` in values.
4. **Be specific but concise.** `paid-social` is better than `ps`. `spring-sale-2025` is better than `campaign1`.
5. **Use a taxonomy.** Standardize your parameter values in a shared document.

### Recommended Taxonomy

| Parameter | Convention | Format |
|-----------|-----------|--------|
| `utm_source` | Platform name, lowercase | `google`, `facebook`, `linkedin`, `klaviyo` |
| `utm_medium` | Channel type matching GA4 grouping rules | `cpc`, `paid-social`, `email`, `display`, `organic-social` |
| `utm_campaign` | `campaign-name` or `campaign-name-date` | `spring-sale-2025`, `retarget-cart-q1` |
| `utm_term` | Keyword or targeting term | `running-shoes`, `brand-keyword` |
| `utm_content` | Creative variant or placement | `video-30s-v2`, `hero-banner`, `cta-red` |

### Examples by Channel

**Google Ads (Search)**
```
?utm_source=google&utm_medium=cpc&utm_campaign=brand-search-2025&utm_term=cogny+analytics&utm_content=rsa-v1
```

**Google Ads (Display)**
```
?utm_source=google&utm_medium=display&utm_campaign=retarget-site-visitors&utm_content=banner-300x250
```

**Google Ads (Performance Max)**
```
?utm_source=google&utm_medium=cpc&utm_campaign=pmax-all-products&utm_content=pmax
```

**Meta Ads (Facebook/Instagram)**
```
?utm_source=facebook&utm_medium=paid-social&utm_campaign=lookalike-purchase-q1&utm_content=carousel-lifestyle-v2
```

**LinkedIn Ads**
```
?utm_source=linkedin&utm_medium=paid-social&utm_campaign=b2b-decision-makers&utm_content=single-image-whitepaper
```

**Email (Klaviyo/Mailchimp)**
```
?utm_source=klaviyo&utm_medium=email&utm_campaign=weekly-newsletter-apr-10&utm_content=header-cta
```

**Organic Social (LinkedIn post)**
```
?utm_source=linkedin&utm_medium=organic-social&utm_campaign=thought-leadership&utm_content=ceo-post-ai-trends
```

**Influencer**
```
?utm_source=creator-jane-doe&utm_medium=paid-social&utm_campaign=influencer-spring-2025&utm_content=instagram-story
```

**Affiliate**
```
?utm_source=partner-techblog&utm_medium=affiliate&utm_campaign=affiliate-program-2025&utm_content=review-article
```

## Auto-Tagging vs Manual Tagging

### Google Ads Auto-Tagging (gclid)

Google Ads appends a `gclid` (Google Click ID) parameter to every ad click URL. When auto-tagging is enabled:

- GA4 automatically attributes the session to Google Ads with correct campaign, ad group, keyword, and match type data
- The `gclid` links GA4 sessions to Google Ads cost data for ROAS reporting
- Source/medium is set to `google / cpc` automatically
- No UTM parameters are needed for standard Google Ads tracking

**When you still need UTMs with Google Ads:**
- Cross-platform reporting tools that do not read `gclid`
- Third-party analytics platforms (Mixpanel, Amplitude, etc.)
- CRM systems that parse UTM parameters from landing page URLs
- When you need `utm_content` to track creative variants in BigQuery
- When auto-tagging is disabled (not recommended)

**Important:** If both `gclid` and UTM parameters are present, GA4 prioritizes `gclid`. The UTM values are ignored for GA4 attribution but may still be captured in the raw URL (accessible via `page_location` event parameter).

### Meta Auto-Tagging (fbclid)

Meta Ads appends an `fbclid` parameter to click URLs. However:

- GA4 does **not** natively parse `fbclid` for attribution
- Without UTM parameters, Meta traffic typically appears as `facebook.com / referral` or `l.facebook.com / referral`
- You **must** use UTM parameters for correct Meta Ads attribution in GA4
- Set UTMs at the ad set or ad level in Meta Ads Manager using URL Parameters

**Meta Ads Manager URL Parameter template:**
```
utm_source=facebook&utm_medium=paid-social&utm_campaign={{campaign.name}}&utm_content={{ad.name}}&utm_term={{adset.name}}
```

Meta supports dynamic parameters: `{{campaign.name}}`, `{{campaign.id}}`, `{{adset.name}}`, `{{adset.id}}`, `{{ad.name}}`, `{{ad.id}}`, `{{placement}}`, `{{site_source_name}}`.

### LinkedIn Insight Tag

The LinkedIn Insight Tag tracks conversions but does not provide click-level attribution to GA4. You **must** use UTM parameters for LinkedIn Ads attribution in GA4.

**LinkedIn Campaign Manager URL Parameter template:**
```
utm_source=linkedin&utm_medium=paid-social&utm_campaign={{CAMPAIGN_NAME}}&utm_content={{CREATIVE_NAME}}
```

LinkedIn supports these macros: `{{CAMPAIGN_ID}}`, `{{CAMPAIGN_NAME}}`, `{{CAMPAIGN_GROUP_ID}}`, `{{CAMPAIGN_GROUP_NAME}}`, `{{CREATIVE_ID}}`, `{{CREATIVE_NAME}}`.

### When to Use Both Auto-Tagging + Manual UTMs

Use both when:

1. **Google Ads + CRM integration:** Auto-tagging feeds GA4, UTMs feed your CRM (HubSpot, Salesforce) for lead attribution
2. **Cross-platform dashboards:** Tools like Looker Studio can use either, but third-party tools often need UTMs
3. **BigQuery analysis:** You want UTM values in `page_location` for custom attribution models beyond GA4's built-in reporting

**Warning:** For Google Ads, if you enable manual tagging alongside auto-tagging, ensure "Allow manual tagging (UTM values) to override auto-tagging (GCLID values)" is **unchecked** in GA4 Admin > Data Streams > Web Stream > Google Tag > Settings. Otherwise UTMs override the richer gclid-based attribution.

## Common UTM Mistakes

### 1. Inconsistent Naming

The single most common UTM problem. GA4 treats each unique string as a separate source.

```
# These create THREE separate sources in GA4:
utm_source=Facebook       # Wrong: capital F
utm_source=facebook       # Correct
utm_source=fb             # Wrong: abbreviation

# These create THREE separate mediums:
utm_medium=Social         # Wrong: not a GA4 channel grouping match
utm_medium=paid_social    # Wrong: underscore (GA4 expects hyphen)
utm_medium=paid-social    # Correct
```

**Fix:** Maintain a shared UTM naming convention document. Enforce it with a URL builder tool or spreadsheet template.

### 2. UTMs on Internal Links

Adding UTM parameters to links within your own site (navigation, banners, CTAs) resets the session source and inflates session counts. Every internal UTM click starts a "new" session attributed to your own site.

```
# NEVER do this:
<a href="/pricing?utm_source=homepage&utm_medium=banner">See Pricing</a>

# Instead, use event parameters or content groupings:
<a href="/pricing" data-tracking="homepage-banner">See Pricing</a>
```

**Detection query:**

```sql
-- Find internal UTM pollution
SELECT
  (SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'page_location') as page_url,
  (SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'page_referrer') as referrer,
  (SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'source') as source,
  (SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'medium') as medium,
  COUNT(*) as occurrences
FROM `project.analytics_123456789.events_*`
WHERE _TABLE_SUFFIX BETWEEN FORMAT_DATE('%Y%m%d', DATE_SUB(CURRENT_DATE(), INTERVAL 30 DAY))
                        AND FORMAT_DATE('%Y%m%d', CURRENT_DATE() - 1)
  AND event_name = 'session_start'
  AND (SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'page_referrer') LIKE '%example.com%'
  AND (SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'source') IS NOT NULL
  AND (SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'source') != '(direct)'
GROUP BY 1, 2, 3, 4
ORDER BY occurrences DESC
```

### 3. Missing utm_medium

Without `utm_medium`, GA4 cannot classify traffic into the correct default channel grouping. Traffic with a source but no medium typically falls into **Unassigned**.

```
# Bad: No medium — goes to Unassigned
?utm_source=facebook&utm_campaign=spring-sale

# Good: Medium present — correctly classified as Paid Social
?utm_source=facebook&utm_medium=paid-social&utm_campaign=spring-sale
```

### 4. URL-Encoded Special Characters

Spaces in UTM values become `%20`, which creates messy source/medium values in reports. Ampersands break the URL structure entirely.

```
# Bad:
utm_campaign=Spring Sale 2025      # Becomes Spring%20Sale%202025
utm_campaign=Q1 - Brand & Perf     # Ampersand breaks URL parsing

# Good:
utm_campaign=spring-sale-2025
utm_campaign=q1-brand-and-perf
```

### 5. UTMs on Organic/SEO Pages

Never add UTM parameters to pages that search engines will crawl. Search engines may index the UTM-tagged URL, splitting your page authority and creating duplicate content. Organic search traffic is automatically attributed by GA4 without any UTM parameters.

### 6. Forgetting to Update UTMs After Campaign Changes

Reusing the same UTM campaign name across different quarters or years without updating it makes historical analysis impossible. Always include a date or version identifier.

```
# Bad: Reused across years
utm_campaign=brand-search

# Good: Time-scoped
utm_campaign=brand-search-q1-2025
```

## UTM Builder Patterns

### URL Template by Channel

**Google Ads (Search):**
```
{landing_page}?utm_source=google&utm_medium=cpc&utm_campaign={campaign_name}&utm_term={keyword}&utm_content={ad_variant}
```

**Google Ads (Display/Video):**
```
{landing_page}?utm_source=google&utm_medium=display&utm_campaign={campaign_name}&utm_content={creative_size_or_variant}
```

**Meta Ads:**
```
{landing_page}?utm_source=facebook&utm_medium=paid-social&utm_campaign={{campaign.name}}&utm_content={{ad.name}}&utm_term={{adset.name}}
```

**LinkedIn Ads:**
```
{landing_page}?utm_source=linkedin&utm_medium=paid-social&utm_campaign={{CAMPAIGN_NAME}}&utm_content={{CREATIVE_NAME}}
```

**Email:**
```
{landing_page}?utm_source={esp_name}&utm_medium=email&utm_campaign={email_campaign_name}&utm_content={link_placement}
```

**Organic Social:**
```
{landing_page}?utm_source={platform}&utm_medium=organic-social&utm_campaign={post_theme}&utm_content={post_variant}
```

### Spreadsheet / Naming Convention Template

Use a shared spreadsheet with these columns to generate UTM URLs at scale:

| Column | Description | Example |
|--------|-------------|---------|
| Landing Page URL | Base URL without parameters | `https://example.com/landing` |
| Source | Platform name | `facebook` |
| Medium | Channel type | `paid-social` |
| Campaign | Campaign name | `spring-sale-2025` |
| Term | Keyword or targeting (optional) | `lookalike-purchase` |
| Content | Creative variant (optional) | `carousel-v2` |
| **Generated URL** | Formula output | Full URL with parameters |

**Spreadsheet formula (Google Sheets):**
```
=A2&"?utm_source="&B2&"&utm_medium="&C2&"&utm_campaign="&D2&IF(E2<>"","&utm_term="&E2,"")&IF(F2<>"","&utm_content="&F2,"")
```

### Bulk URL Generation

For campaigns with many ad variants, generate URLs programmatically:

```python
import urllib.parse

base_url = "https://example.com/landing"
variants = [
    {"source": "facebook", "medium": "paid-social", "campaign": "spring-sale-2025", "content": "carousel-v1"},
    {"source": "facebook", "medium": "paid-social", "campaign": "spring-sale-2025", "content": "carousel-v2"},
    {"source": "facebook", "medium": "paid-social", "campaign": "spring-sale-2025", "content": "video-15s"},
]

for v in variants:
    params = urllib.parse.urlencode({
        "utm_source": v["source"],
        "utm_medium": v["medium"],
        "utm_campaign": v["campaign"],
        "utm_content": v["content"],
    })
    print(f"{base_url}?{params}")
```

## Validation with BigQuery

### Find All Unique Source/Medium Combinations

Identify every source/medium pair in your data to spot inconsistencies:

```sql
SELECT
  traffic_source.source,
  traffic_source.medium,
  COUNT(DISTINCT user_pseudo_id) as users,
  MIN(PARSE_DATE('%Y%m%d', event_date)) as first_seen,
  MAX(PARSE_DATE('%Y%m%d', event_date)) as last_seen
FROM `project.analytics_123456789.events_*`
WHERE _TABLE_SUFFIX BETWEEN FORMAT_DATE('%Y%m%d', DATE_SUB(CURRENT_DATE(), INTERVAL 90 DAY))
                        AND FORMAT_DATE('%Y%m%d', CURRENT_DATE() - 1)
GROUP BY 1, 2
ORDER BY users DESC
```

### Find Orphaned/Inconsistent UTMs

Detect variations in source naming (e.g., `facebook` vs `Facebook` vs `fb`):

```sql
WITH source_variants AS (
  SELECT
    LOWER(traffic_source.source) as normalized_source,
    traffic_source.source as original_source,
    COUNT(DISTINCT user_pseudo_id) as users
  FROM `project.analytics_123456789.events_*`
  WHERE _TABLE_SUFFIX BETWEEN FORMAT_DATE('%Y%m%d', DATE_SUB(CURRENT_DATE(), INTERVAL 90 DAY))
                          AND FORMAT_DATE('%Y%m%d', CURRENT_DATE() - 1)
    AND traffic_source.source IS NOT NULL
  GROUP BY 1, 2
)

SELECT
  normalized_source,
  ARRAY_AGG(STRUCT(original_source, users) ORDER BY users DESC) as variants,
  COUNT(*) as variant_count
FROM source_variants
GROUP BY 1
HAVING COUNT(*) > 1
ORDER BY variant_count DESC
```

### Check Default Channel Grouping Assignment

Verify which channel group GA4 assigns to each source/medium pair:

```sql
SELECT
  traffic_source.source,
  traffic_source.medium,
  CASE
    WHEN traffic_source.source = '(direct)' AND (traffic_source.medium IS NULL OR traffic_source.medium IN ('(not set)', '(none)')) THEN 'Direct'
    WHEN traffic_source.medium = 'organic' THEN 'Organic Search'
    WHEN REGEXP_CONTAINS(traffic_source.medium, r'^(cpc|ppc|paidsearch)$') THEN 'Paid Search'
    WHEN REGEXP_CONTAINS(traffic_source.medium, r'^(display|cpm|banner)$') THEN 'Display'
    WHEN REGEXP_CONTAINS(traffic_source.medium, r'^(paid-social|paidsocial|paid_social)$') THEN 'Paid Social'
    WHEN REGEXP_CONTAINS(traffic_source.medium, r'^(social|social-network|social-media|sm|social_network|social_media|organic-social)$') THEN 'Organic Social'
    WHEN traffic_source.medium = 'email' THEN 'Email'
    WHEN traffic_source.medium = 'affiliate' THEN 'Affiliates'
    WHEN traffic_source.medium = 'referral' THEN 'Referral'
    WHEN traffic_source.medium = 'audio' THEN 'Audio'
    WHEN REGEXP_CONTAINS(traffic_source.medium, r'^(sms|sms_message)$') THEN 'SMS'
    WHEN REGEXP_CONTAINS(traffic_source.medium, r'^(video|youtube)$') THEN 'Video'
    WHEN REGEXP_CONTAINS(IFNULL(traffic_source.name, ''), r'cross-network') THEN 'Cross-network'
    ELSE 'Unassigned'
  END as predicted_channel_group,
  COUNT(DISTINCT user_pseudo_id) as users
FROM `project.analytics_123456789.events_*`
WHERE _TABLE_SUFFIX BETWEEN FORMAT_DATE('%Y%m%d', DATE_SUB(CURRENT_DATE(), INTERVAL 30 DAY))
                        AND FORMAT_DATE('%Y%m%d', CURRENT_DATE() - 1)
GROUP BY 1, 2, 3
ORDER BY users DESC
```

### Find Internal UTM Pollution

Detect sessions where UTMs were triggered from your own domain (internal links with UTMs):

```sql
SELECT
  (SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'page_location') as landing_page,
  (SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'page_referrer') as referrer,
  (SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'source') as session_source,
  (SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'medium') as session_medium,
  (SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'campaign') as session_campaign,
  COUNT(*) as sessions
FROM `project.analytics_123456789.events_*`
WHERE _TABLE_SUFFIX BETWEEN FORMAT_DATE('%Y%m%d', DATE_SUB(CURRENT_DATE(), INTERVAL 30 DAY))
                        AND FORMAT_DATE('%Y%m%d', CURRENT_DATE() - 1)
  AND event_name = 'session_start'
  -- Replace example.com with your domain
  AND REGEXP_CONTAINS(
    (SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'page_referrer'),
    r'example\.com'
  )
  AND REGEXP_CONTAINS(
    (SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'page_location'),
    r'utm_'
  )
GROUP BY 1, 2, 3, 4, 5
ORDER BY sessions DESC
```

### Find Sessions Missing utm_medium

Identify campaigns that are missing the critical `utm_medium` parameter:

```sql
SELECT
  (SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'source') as source,
  (SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'campaign') as campaign,
  COUNT(*) as sessions
FROM `project.analytics_123456789.events_*`
WHERE _TABLE_SUFFIX BETWEEN FORMAT_DATE('%Y%m%d', DATE_SUB(CURRENT_DATE(), INTERVAL 30 DAY))
                        AND FORMAT_DATE('%Y%m%d', CURRENT_DATE() - 1)
  AND event_name = 'session_start'
  AND (SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'source') IS NOT NULL
  AND (SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'source') != '(direct)'
  AND (
    (SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'medium') IS NULL
    OR (SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'medium') IN ('(not set)', '(none)', '')
  )
GROUP BY 1, 2
ORDER BY sessions DESC
```

## Next Steps

- [GA4 BigQuery Export Schema](/docs/ga4-bigquery-export-schema) - Understand the full GA4 export schema
- [Connect Your Data Sources](/docs/connect-your-data-sources) - Your BigQuery warehouse, the GA4 and Search Console links, and Bring Your Own Data
- [Integration Catalog and Setup Guides](/docs/integration-catalog-and-setup-guides) - Every integration, how it connects, and the setup notes

## Claude Code Skill

This UTM strategy reference is also available as a free Claude Code skill — use it directly in your terminal to build UTM URLs, audit your naming conventions, and validate attribution data:

```bash
# Install
curl -sSL https://raw.githubusercontent.com/cognyai/claude-code-marketing-skills/main/install.sh | bash

# Use
/utm-builder                          # Full UTM strategy overview
/utm-builder naming conventions       # Show naming convention rules
/utm-builder channel grouping         # GA4 default channel grouping rules
/utm-builder audit                    # BigQuery validation queries
```

[View on GitHub →](https://github.com/cognyai/claude-code-marketing-skills/tree/main/skills/utm-builder)

## Resources

- **Google UTM Builder:** [ga-dev-tools.google/ga4/campaign-url-builder](https://ga-dev-tools.google/ga4/campaign-url-builder/)
- **GA4 Default Channel Grouping:** [support.google.com/analytics/answer/9756891](https://support.google.com/analytics/answer/9756891)
- **GA4 BigQuery Export Schema:** [support.google.com/analytics/answer/7029846](https://support.google.com/analytics/answer/7029846)
- **Google Ads Auto-Tagging:** [support.google.com/google-ads/answer/3095550](https://support.google.com/google-ads/answer/3095550)
- **Meta URL Parameters:** [facebook.com/business/help/1016122818401732](https://www.facebook.com/business/help/1016122818401732)
- **Claude Code Marketing Skills:** [github.com/cognyai/claude-code-marketing-skills](https://github.com/cognyai/claude-code-marketing-skills)

---

In this section:

- Conversion Tracking Debugger Reference: https://cogny.com/docs/conversion-tracking-debugger
- Core Web Vitals Reference: https://cogny.com/docs/core-web-vitals
- GA4 BigQuery Export Schema Reference: https://cogny.com/docs/ga4-bigquery-export-schema
- GA4 Event Implementation Reference: https://cogny.com/docs/ga4-event-implementation
- Google Ads Query Language (GAQL) Reference: https://cogny.com/docs/gaql-reference
- Google Ads Scripts Reference: https://cogny.com/docs/google-ads-scripts
- GTM Event Tracking & Setup Reference: https://cogny.com/docs/gtm-event-tracking
- Meta Conversions API (CAPI) Setup Reference: https://cogny.com/docs/meta-conversions-api
- Schema.org Structured Data Reference: https://cogny.com/docs/structured-data-reference
- **UTM Parameter Strategy & Builder Reference** (this page): https://cogny.com/docs/utm-strategy

Source page: https://cogny.com/docs/utm-strategy  
All documentation: https://cogny.com/docs  
Cogny for agents: https://cogny.com/llms.txt
