utm-link-generator · git:20260410.ab060dd · 2026-04-10 · sha256 0e12b939f841799e
utm-link-generator git:20260410.ab060ddA
Immutable. This exact content is served forever at /api/v1/blob/0e12b939f841799e.
---
name: utm-link-generator
description: Generates properly tagged UTM links with consistent naming conventions. Maintains a UTM registry file (utm-registry.json) to enforce naming consistency, prevent duplicates, generate short links, and output ready-to-copy links for LinkedIn, email, social, and ads.
tools: Read, Write, Bash
user_invocable: true
---
# 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
1. **Generate UTM-tagged URLs** with validated, consistent parameters
2. **Maintain a utm-registry.json** file that tracks all generated links and enforces naming conventions
3. **Prevent duplicates** -- warns when a similar link already exists in the registry
4. **Platform-specific formatting** -- outputs links optimized for LinkedIn, email, social, and ad platforms
5. **Bulk generation** -- create multiple variants for A/B testing or multi-channel campaigns
6. **Validation** -- rejects malformed URLs, empty parameters, and convention violations
7. **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
1. **All lowercase** -- Never `LinkedIn`, always `linkedin`
2. **Hyphens for spaces** -- Never `product launch`, always `product-launch`
3. **No special characters** -- Only alphanumeric and hyphens
4. **No trailing/leading hyphens** -- Never `-campaign-name-`
5. **No double hyphens** -- Never `my--campaign`
6. **Maximum length**: 50 characters per parameter value
7. **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` | LinkedIn |
| `facebook` | `fb`, `meta`, `facebook-ads` | Facebook / Meta |
| `instagram` | `ig`, `insta` | Instagram |
| `twitter` | `x`, `x-com`, `tw` | X (Twitter) |
| `youtube` | `yt`, `you-tube` | YouTube |
| `tiktok` | `tt`, `tik-tok` | TikTok |
| `reddit` | `rd` | Reddit |
| `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-launch`
- `2026-03-spring-webinar`
- `q2-2026-brand-awareness-linkedin`
- `evergreen-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:
1. **Convert to lowercase** -- `LinkedIn` becomes `linkedin`
2. **Replace spaces with hyphens** -- `product launch` becomes `product-launch`
3. **Strip special characters** -- Remove anything not `[a-z0-9-]`
4. **Check against canonical lists** -- Auto-correct known aliases (e.g., `fb` to `facebook`)
5. **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
6. **Validate URL** -- Ensure the destination URL is well-formed (has protocol, valid domain)
7. **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:
1. **Exact duplicates** -- Same URL + same parameters. Warn and return the existing link.
2. **Near duplicates** -- Same URL + same campaign + different content/medium. Warn but allow (this is often intentional for multi-channel tracking).
3. **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
1. Parse each line into (URL, platforms, campaign)
2. Validate and normalize all parameters
3. Generate all variant links for each URL/platform combination
4. Deduplicate across the batch
5. Update registry with all new links
6. 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
1. **One registry, one truth** -- All UTM links flow through the registry. No ad-hoc link creation.
2. **Convention over configuration** -- Canonical lists prevent naming drift before it starts.
3. **Auto-correction over rejection** -- When possible, fix the input rather than blocking the user.
4. **Platform awareness** -- Different platforms need different utm_content values to distinguish placements.
5. **Date-prefixed campaigns** -- Makes it trivial to filter analytics by time period.
6. **Audit trail** -- Every link is timestamped and attributed in the registry.