UTM Link Generator
A disciplined UTM link generation system that enforces consistent naming conventions across all campaigns, maintains a registry to prevent duplicates and naming drift, and outputs platform-ready links. This is not a simple URL concatenator -- it is a naming governance system for marketing attribution.
Why This Exists
UTM chaos is the number one cause of broken marketing attribution. When team members use "linkedin" vs "LinkedIn" vs "linked-in" vs "li" as a source, your analytics become useless. This skill enforces a single source of truth: the UTM registry.
Capabilities
- Generate UTM-tagged URLs with validated, consistent parameters
- Maintain a utm-registry.json file that tracks all generated links and enforces naming conventions
- Prevent duplicates -- warns when a similar link already exists in the registry
- Platform-specific formatting -- outputs links optimized for LinkedIn, email, social, and ad platforms
- Bulk generation -- create multiple variants for A/B testing or multi-channel campaigns
- Validation -- rejects malformed URLs, empty parameters, and convention violations
- Reporting -- generates campaign tracking summaries from the registry
UTM Parameter Reference
| Parameter | Required | Purpose | Example |
|---|
| Yes | Where the traffic comes from | , , |
| Yes | Marketing medium | , , , |
| Yes | Campaign identifier | |
| No | Paid keyword (search ads) | project-management-software
|
| No | Differentiates similar links | , , |
| No | Campaign ID for GA4 | |
Naming Conventions (Enforced)
These conventions are non-negotiable. The skill will reject or auto-correct any violations.
General Rules
- All lowercase -- Never , always
- Hyphens for spaces -- Never , always
- No special characters -- Only alphanumeric and hyphens
- No trailing/leading hyphens -- Never
- No double hyphens -- Never
- Maximum length: 50 characters per parameter value
- Date format in campaigns: or prefix
Source Values (Canonical List)
Only these source values are allowed. Any new source must be explicitly added to the registry.
| Canonical Source | Aliases (auto-corrected) | Platform |
|---|
| , , | Google Ads / Organic |
| , , | LinkedIn |
| , , | Facebook / Meta |
| , | Instagram |
| , , | X (Twitter) |
| , | YouTube |
| , | TikTok |
| | Reddit |
| , , | Email campaigns |
| , | Newsletter specifically |
| , | Bing / Microsoft |
| , | Direct traffic |
| , | Referral traffic |
| , | Podcast channels |
| | Slack communities |
| , | Product Hunt |
| , | Hacker News |
Medium Values (Canonical List)
| Canonical Medium | Aliases (auto-corrected) | Use Case |
|---|
| , , | Paid search / paid social clicks |
| , | Display / impression campaigns |
| , | Email campaigns |
| , | Organic social posts |
| , | Organic search traffic |
| , , | Referral / partner links |
| , , | Video ad placements |
| , | Retargeting campaigns |
| , , | Content marketing |
| , , | Webinar / event registrations |
| , | Podcast mentions / ads |
| , | SMS / text campaigns |
| , | QR code / print materials |
| , | Push notifications |
Campaign Naming Pattern
Campaigns follow this structure:
[quarter-or-month]-[year]-[campaign-name]-[optional-variant]
Examples:
q2-2026-brand-awareness-linkedin
- (for always-on campaigns, skip the date prefix)
Execution Protocol
Step 1: Accept Input
The user provides some or all of these:
- Destination URL (required) -- The page they want to link to
- Source (required) -- Where the traffic comes from
- Medium (required) -- The marketing channel type
- Campaign (required) -- Campaign name
- Term (optional) -- Keyword for paid search
- Content (optional) -- Link differentiator
- Platforms (optional) -- Which platforms to generate links for (default: all relevant)
If the user provides a brief like "I need links for our Q1 product launch campaign on LinkedIn and email pointing to our pricing page," extract the parameters from natural language.
Step 2: Validate and Normalize
For each parameter:
- Convert to lowercase -- becomes
- Replace spaces with hyphens -- becomes
- Strip special characters -- Remove anything not
- Check against canonical lists -- Auto-correct known aliases (e.g., to )
- Reject unknown values -- If a source or medium is not in the canonical list and is not a reasonable addition, warn the user and suggest the closest match
- Validate URL -- Ensure the destination URL is well-formed (has protocol, valid domain)
- Check length -- Ensure no parameter exceeds 50 characters
Report any corrections made:
## Parameter Validation
- Source: "LinkedIn" -> "linkedin" (normalized to lowercase)
- Medium: "paid" -> "cpc" (corrected to canonical value)
- Campaign: "Q1 Product Launch!" -> "q1-product-launch" (normalized)
- URL: https://example.com/pricing -- Valid
Step 3: Check Registry for Duplicates
Read the utm-registry.json file (or create it if it does not exist). Check for:
- Exact duplicates -- Same URL + same parameters. Warn and return the existing link.
- Near duplicates -- Same URL + same campaign + different content/medium. Warn but allow (this is often intentional for multi-channel tracking).
- Naming conflicts -- Same campaign name but different capitalization or hyphenation. Block and show the existing convention.
Step 4: Generate Links
Build the UTM-tagged URL:
{base_url}?utm_source={source}&utm_medium={medium}&utm_campaign={campaign}[&utm_term={term}][&utm_content={content}][&utm_id={id}]
Rules:
- If the base URL already has query parameters, append with not
- URL-encode any parameter values that contain special characters
- Preserve the base URL's existing query parameters and fragment
Step 5: Generate Platform-Specific Variants
For each platform requested, generate an optimized link:
LinkedIn
## LinkedIn
**Post Link** (organic social):
https://example.com/pricing?utm_source=linkedin&utm_medium=social&utm_campaign=q1-2026-product-launch&utm_content=organic-post
**Ad Link** (paid):
https://example.com/pricing?utm_source=linkedin&utm_medium=cpc&utm_campaign=q1-2026-product-launch&utm_content=sponsored-post
**Message Link** (InMail/DM):
https://example.com/pricing?utm_source=linkedin&utm_medium=social&utm_campaign=q1-2026-product-launch&utm_content=inmail
**Profile Link** (bio/featured):
https://example.com/pricing?utm_source=linkedin&utm_medium=social&utm_campaign=q1-2026-product-launch&utm_content=profile-link
Email
## Email
**Primary CTA**:
https://example.com/pricing?utm_source=email&utm_medium=email&utm_campaign=q1-2026-product-launch&utm_content=primary-cta
**Secondary CTA**:
https://example.com/pricing?utm_source=email&utm_medium=email&utm_campaign=q1-2026-product-launch&utm_content=secondary-cta
**Header Link**:
https://example.com/pricing?utm_source=email&utm_medium=email&utm_campaign=q1-2026-product-launch&utm_content=header-link
**Footer Link**:
https://example.com/pricing?utm_source=email&utm_medium=email&utm_campaign=q1-2026-product-launch&utm_content=footer-link
Social (General)
## Social
**Twitter/X**:
https://example.com/pricing?utm_source=twitter&utm_medium=social&utm_campaign=q1-2026-product-launch
**Facebook**:
https://example.com/pricing?utm_source=facebook&utm_medium=social&utm_campaign=q1-2026-product-launch
**Instagram** (bio link):
https://example.com/pricing?utm_source=instagram&utm_medium=social&utm_campaign=q1-2026-product-launch&utm_content=bio-link
**Reddit**:
https://example.com/pricing?utm_source=reddit&utm_medium=social&utm_campaign=q1-2026-product-launch
Ads (Paid)
## Paid Ads
**Google Ads**:
https://example.com/pricing?utm_source=google&utm_medium=cpc&utm_campaign=q1-2026-product-launch&utm_term={keyword}
**LinkedIn Ads**:
https://example.com/pricing?utm_source=linkedin&utm_medium=cpc&utm_campaign=q1-2026-product-launch
**Facebook/Meta Ads**:
https://example.com/pricing?utm_source=facebook&utm_medium=cpc&utm_campaign=q1-2026-product-launch
Note: For Google Ads, use {keyword} as a dynamic placeholder that Google will auto-replace.
Step 6: Update Registry
Append all generated links to utm-registry.json:
json
{
"version": "1.0",
"lastUpdated": "2026-04-10T12:00:00Z",
"namingConventions": {
"sources": ["google", "linkedin", "facebook", "instagram", "twitter", "email", "newsletter"],
"mediums": ["cpc", "cpm", "email", "social", "organic", "referral", "video", "retargeting"]
},
"campaigns": [
{
"name": "q1-2026-product-launch",
"createdAt": "2026-04-10T12:00:00Z",
"description": "Q1 2026 product launch campaign",
"links": [
{
"id": "utm-001",
"url": "https://example.com/pricing?utm_source=linkedin&utm_medium=social&utm_campaign=q1-2026-product-launch&utm_content=organic-post",
"source": "linkedin",
"medium": "social",
"campaign": "q1-2026-product-launch",
"content": "organic-post",
"platform": "linkedin",
"variant": "organic-post",
"createdAt": "2026-04-10T12:00:00Z",
"status": "active"
}
]
}
],
"stats": {
"totalLinks": 1,
"totalCampaigns": 1,
"sourcesUsed": ["linkedin"],
"mediumsUsed": ["social"]
}
}
Step 7: Output Summary
Present the final output in a clean, copy-paste-ready format:
## UTM Links Generated
**Campaign**: q1-2026-product-launch
**Destination**: https://example.com/pricing
**Generated**: 2026-04-10
### Ready-to-Copy Links
| Platform | Variant | Link |
|----------|---------|------|
| LinkedIn (organic) | organic-post | [full URL] |
| LinkedIn (paid) | sponsored-post | [full URL] |
| Email (primary CTA) | primary-cta | [full URL] |
| Email (secondary CTA) | secondary-cta | [full URL] |
| Twitter | default | [full URL] |
| Facebook | default | [full URL] |
### Registry Updated
- New links added: 6
- Total links in registry: 6
- Registry file: ./utm-registry.json
### Validation Report
- All parameters normalized to convention
- No duplicate links found
- No naming conflicts detected
Bulk Generation Mode
For generating links across many campaigns or many URLs at once.
Input Format (Bulk)
The user can provide a table or list:
Generate UTM links for these pages:
- /pricing -- linkedin, email, twitter -- q1-2026-product-launch
- /demo -- linkedin, google-ads -- q1-2026-demo-push
- /case-studies -- email, linkedin -- q1-2026-social-proof
Processing
- Parse each line into (URL, platforms, campaign)
- Validate and normalize all parameters
- Generate all variant links for each URL/platform combination
- Deduplicate across the batch
- Update registry with all new links
- Output a master table
Output Format (Bulk)
## Bulk UTM Generation -- 3 campaigns, 8 platform variants, 24 total links
| Campaign | URL | Platform | Variant | Full Link |
|----------|-----|----------|---------|-----------|
| q1-2026-product-launch | /pricing | linkedin-organic | organic-post | [URL] |
| q1-2026-product-launch | /pricing | linkedin-paid | sponsored-post | [URL] |
| q1-2026-product-launch | /pricing | email | primary-cta | [URL] |
| ... | ... | ... | ... | ... |
Registry updated: 24 new links added.
Registry Management Commands
The user can also invoke this skill for registry operations:
Audit Registry
Reads utm-registry.json and reports:
- Total campaigns and links
- Naming convention violations (if any crept in manually)
- Stale campaigns (older than 6 months with no new links)
- Unused sources or mediums
- Duplicate or near-duplicate links
Add Custom Source/Medium
"Add 'partnerstack' as a UTM source"
Adds the new value to the canonical list in the registry and documents it.
Campaign Report
"Show me all links for the q1-2026-product-launch campaign"
Filters the registry and displays all links for that campaign, grouped by platform.
Export for Google Sheets
"Export my UTM registry as CSV"
Generates a CSV file with columns: Campaign, URL, Source, Medium, Content, Term, Full Link, Created Date, Status.
Error Handling
| Error | Response |
|---|
| Invalid URL (no protocol) | Auto-prepend and warn |
| Unknown source | Suggest closest canonical match, ask for confirmation |
| Unknown medium | Suggest closest canonical match, ask for confirmation |
| Campaign name too long | Suggest abbreviated version |
| Duplicate link exists | Show existing link, ask if user wants to create anyway |
| Registry file missing | Create new registry with default conventions |
| Registry file corrupted | Attempt to parse what exists, back up, create fresh |
| Base URL has existing UTMs | Strip existing UTM params, warn user, apply new ones |
Integration Notes
- Google Analytics 4: All parameters map directly to GA4 dimensions. utm_id maps to campaign_id.
- HubSpot: Source and medium map to HubSpot's Original Source properties.
- Salesforce: Campaign name can map to Salesforce Campaign records via integration.
- Mixpanel / Amplitude: UTM params auto-captured on page load via standard SDKs.
Best Practices Embedded in This Skill
- One registry, one truth -- All UTM links flow through the registry. No ad-hoc link creation.
- Convention over configuration -- Canonical lists prevent naming drift before it starts.
- Auto-correction over rejection -- When possible, fix the input rather than blocking the user.
- Platform awareness -- Different platforms need different utm_content values to distinguish placements.
- Date-prefixed campaigns -- Makes it trivial to filter analytics by time period.
- Audit trail -- Every link is timestamped and attributed in the registry.