utm-link-generator
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 |
|---|---|---|---|
utm_source |
Yes | Where the traffic comes from | linkedin, google, newsletter |
utm_medium |
Yes | Marketing medium | cpc, email, social, organic |
utm_campaign |
Yes | Campaign identifier | q1-2026-product-launch |
utm_term |
No | Paid keyword (search ads) | project-management-software |
utm_content |
No | Differentiates similar links | hero-cta, sidebar-banner, variant-a |
utm_id |
No | Campaign ID for GA4 | camp-2026-q1-001 |
Naming Conventions (Enforced)
These conventions are non-negotiable. The skill will reject or auto-correct any violations.
General Rules
- All lowercase -- Never
LinkedIn, alwayslinkedin - Hyphens for spaces -- Never
product launch, alwaysproduct-launch - No special characters -- Only alphanumeric and hyphens
- No trailing/leading hyphens -- Never
-campaign-name- - No double hyphens -- Never
my--campaign - Maximum length: 50 characters per parameter value
- Date format in campaigns:
q[N]-[YYYY]or[YYYY]-[MM]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 |
goog, google-ads, adwords |
Google Ads / Organic |
linkedin |
li, linked-in, linkedin-ads |
|
facebook |
fb, meta, facebook-ads |
Facebook / Meta |
instagram |
ig, insta |
|
twitter |
x, x-com, tw |
X (Twitter) |
youtube |
yt, you-tube |
YouTube |
tiktok |
tt, tik-tok |
TikTok |
reddit |
rd |
|
email |
mail, e-mail, em |
Email campaigns |
newsletter |
nl, news-letter |
Newsletter specifically |
bing |
microsoft-ads, msn |
Bing / Microsoft |
direct |
none, (direct) |
Direct traffic |
referral |
ref, partner |
Referral traffic |
podcast |
pod, spotify |
Podcast channels |
slack |
sl |
Slack communities |
producthunt |
ph, product-hunt |
Product Hunt |
hackernews |
hn, hacker-news |
Hacker News |
Medium Values (Canonical List)
| Canonical Medium | Aliases (auto-corrected) | Use Case |
|---|---|---|
cpc |
ppc, paid-click, cost-per-click |
Paid search / paid social clicks |
cpm |
display, paid-impression |
Display / impression campaigns |
email |
e-mail, mail |
Email campaigns |
social |
social-media, organic-social |
Organic social posts |
organic |
seo, organic-search |
Organic search traffic |
referral |
ref, partner, affiliate |
Referral / partner links |
video |
vid, youtube, pre-roll |
Video ad placements |
retargeting |
remarketing, retarget |
Retargeting campaigns |
content |
blog, article, content-syndication |
Content marketing |
webinar |
web, event, virtual-event |
Webinar / event registrations |
podcast |
pod, audio |
Podcast mentions / ads |
sms |
text, mms |
SMS / text campaigns |
qr |
qr-code, print |
QR code / print materials |
push |
push-notification, notification |
Push notifications |
Campaign Naming Pattern
Campaigns follow this structure:
[quarter-or-month]-[year]-[campaign-name]-[optional-variant]
Examples:
q1-2026-product-launch2026-03-spring-webinarq2-2026-brand-awareness-linkedinevergreen-demo-request(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 --
LinkedInbecomeslinkedin - Replace spaces with hyphens --
product launchbecomesproduct-launch - Strip special characters -- Remove anything not
[a-z0-9-] - Check against canonical lists -- Auto-correct known aliases (e.g.,
fbtofacebook) - 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
¬? - 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
**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
**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:
{
"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
"Audit my UTM 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 https:// 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.