GTM Implementation Guide

How to Track UTM Parameters in Salesforce Forms

A Complete GTM Guide for Clean Attribution

If you’re running paid campaigns or multi-channel marketing, your success depends on one thing: knowing exactly where your leads come from. Yet most Salesforce setups miss this completely. Leads show up — but attribution is broken. Campaigns look like “direct.” ROI becomes guesswork. The fix? Proper UTM tracking — implemented correctly, end to end.

Level: Intermediate Prerequisites: Google Tag Manager Salesforce Web-to-Lead

In This Guide

You’ll learn how to:

  • Capture UTM parameters reliably from any traffic source
  • Send them to Salesforce via hidden form fields
  • Avoid the most common attribution mistakes
  • Choose the right storage strategy (sessionStorage vs. cookies)
  • Implement everything cleanly using Google Tag Manager (GTM)

What Are UTM Parameters?

UTM parameters are query string tags added to URLs to track the origin and performance of your marketing traffic. When a user clicks a tagged link, these parameters are passed in the URL and can be captured by your analytics tools — and critically, by your CRM.

Example URL

Tagged URL
https://example.com/?utm_source=google
                    &utm_medium=cpc
                    &utm_campaign=spring_sale
                    &utm_term=crm+software
                    &utm_content=banner_v1

Here’s what each parameter tracks:

Parameter Purpose Example Value
utm_source Where traffic came from google, linkedin, newsletter
utm_medium The marketing channel cpc, email, social, organic
utm_campaign Campaign name or promotion spring_sale, q4_launch
utm_term Paid keyword (optional) crm+software, best+tool
utm_content Ad variation / creative (optional) banner_v1, cta_blue

When captured correctly, these allow you to tie every lead in Salesforce back to its precise origin — whether that’s a Google ad, a LinkedIn post, or an email campaign.

Why Most Salesforce UTM Tracking Fails

The most common approach developers take follows this flawed logic:

⚠️
The Flawed Logic “Store UTMs if they exist — otherwise keep previous values.”

This seems reasonable but leads to data contamination. Here’s a real example:

First Visit (Google Ads)
utm_source   = google
utm_medium   = cpc
utm_campaign = spring_sale
utm_term     = crm+software
utm_content  = banner_v1
Second Visit (LinkedIn Ads)
utm_source   = linkedin
utm_medium   = paid-social
(no campaign/term/content)

Result with broken logic — contaminated data:

Contaminated Lead Data
source   = linkedin    ✓ correct
medium   = paid-social  ✓ correct
campaign = spring_sale  ✗ WRONG — from previous session
term     = crm+software ✗ WRONG — from previous session
content  = banner_v1   ✗ WRONG — from previous session

You’ve now mixed two separate campaigns into a single lead record — making it impossible to accurately attribute the conversion.

The Correct Attribution Logic

The Key Principle Treat UTMs as a single atomic attribution set. When a new campaign visit is detected, overwrite ALL UTM fields — including clearing the ones that are absent.

A “new campaign visit” is detected when the URL contains any of: utm_source, utm_medium, or utm_campaign.

The result of correct logic:

  • New values → stored
  • Missing values → blank (not inherited from previous visit)
  • Old values → always cleared on new campaign visit
  • No cross-contamination between campaigns

This is the single most important principle in this entire setup. Everything else flows from it.


Step 1

Choose Your Storage Strategy

Before writing any GTM code, decide where UTM values will be stored between page views. This decision affects your entire implementation and depends on your sales cycle.

Criteria SessionStorage Cookies
Persistence Ends when tab/session closes Configurable (e.g. 30–90 days)
Cross-session tracking No Yes
Implementation complexity Simple Medium
Risk of stale data Low Medium
Consent requirements Minimal Often required (GDPR/CCPA)
Best use case Same-session conversions Long sales cycles

Use SessionStorage when:

  • Users typically convert in the same browsing session
  • You want the cleanest, simplest implementation
  • You want attribution to automatically reset on a new session
  • Your product has a short consideration cycle (e.g., SaaS free trial, demo request)

Use Cookies when:

  • Your sales cycle spans multiple days or sessions
  • Users research before returning to convert
  • You need to support multi-session attribution
  • You have a consent management platform (CMP) in place for GDPR/CCPA compliance
💡
Recommendation For most B2B SaaS companies, SessionStorage is sufficient and far simpler to implement. Start there, and only switch to cookies if your analytics show significant multi-session conversion patterns.
Step 2

Add Hidden Fields to Your Form

Your lead capture form must include hidden input fields for each UTM parameter. These fields will be populated by JavaScript and submitted alongside the visible form fields into Salesforce.

Add this HTML inside your form element:

HTML — Hidden Form Fields
<input type="hidden" name="utm_source"   id="utm_source">
<input type="hidden" name="utm_medium"   id="utm_medium">
<input type="hidden" name="utm_campaign" id="utm_campaign">
<input type="hidden" name="utm_term"     id="utm_term">
<input type="hidden" name="utm_content"  id="utm_content">
⚠️
Salesforce Field Mapping Ensure these field names exactly match your Salesforce Lead object field API names. You may need to create custom fields in Salesforce if they don’t exist. Navigate to Setup → Object Manager → Lead → Fields & Relationships to verify.
Step 3

Capture & Store UTMs in GTM

Create a Custom HTML Tag in GTM with the following code. Set the trigger to fire on All Pages so UTMs are captured on the very first page load — before the user navigates anywhere.

Option A: SessionStorage (Recommended)

GTM Custom HTML Tag — SessionStorage
(function() {
  var keys = [
    'utm_source', 'utm_medium', 'utm_campaign',
    'utm_term', 'utm_content'
  ];
  var params = new URLSearchParams(window.location.search);

  // Detect a new campaign visit
  var hasNewAttribution =
    params.has('utm_source') ||
    params.has('utm_medium') ||
    params.has('utm_campaign');

  if (hasNewAttribution) {
    // Overwrite ALL keys — even absent ones get cleared
    keys.forEach(function(key) {
      sessionStorage.setItem(key, params.get(key) || '');
    });
  }
})();

Option B: Cookies (Multi-session)

GTM Custom HTML Tag — Cookies
(function() {
  function setCookie(name, value, days) {
    var d = new Date();
    d.setTime(d.getTime() + (days * 24 * 60 * 60 * 1000));
    document.cookie = name + '=' + encodeURIComponent(value)
      + ';path=/;expires=' + d.toUTCString()
      + ';SameSite=Lax';
  }

  var keys = [
    'utm_source', 'utm_medium', 'utm_campaign',
    'utm_term', 'utm_content'
  ];
  var params = new URLSearchParams(window.location.search);

  var hasNewAttribution =
    params.has('utm_source') ||
    params.has('utm_medium') ||
    params.has('utm_campaign');

  if (hasNewAttribution) {
    keys.forEach(function(key) {
      setCookie(key, params.get(key) || '', 90); // 90-day window
    });
  }
})();
Step 4

Create GTM Variables

For each UTM field, create a Custom JavaScript Variable in GTM. These variables will be referenced in your form populate tag.

Create 5 variables named: JS - UTM Source, JS - UTM Medium, JS - UTM Campaign, JS - UTM Term, JS - UTM Content

Each variable follows this pattern (example for utm_source):

GTM Variable — SessionStorage
// Variable name: JS - UTM Source
function() {
  return sessionStorage.getItem('utm_source') || '';
}

For the cookie-based implementation, use:

GTM Variable — Cookie version
// Variable name: JS - UTM Source (cookie version)
function() {
  var match = document.cookie.match(
    new RegExp('(?:^|; )utm_source=([^;]*)')
  );
  return match ? decodeURIComponent(match[1]) : '';
}
Step 5

Populate Hidden Form Fields

Create a second Custom HTML Tag to read the GTM variables and inject them into your form’s hidden fields. This must fire AFTER the DOM has loaded.

Tag settings: Type → Custom HTML  |  Trigger → DOM Ready (form page only, e.g. /contact or /demo)

GTM Custom HTML Tag — Form Populate
(function() {
  function setField(name, value) {
    var el = document.querySelector('input[name="' + name + '""]');
    if (el) { el.value = value || ''; }
  }

  function populate() {
    setField('utm_source',   {{JS - UTM Source}});
    setField('utm_medium',   {{JS - UTM Medium}});
    setField('utm_campaign', {{JS - UTM Campaign}});
    setField('utm_term',     {{JS - UTM Term}});
    setField('utm_content',  {{JS - UTM Content}});
  }

  // Populate on page load
  populate();

  // Re-populate before submit (for dynamic forms / SPA frameworks)
  document.addEventListener('wpcf7beforesubmit', populate);
  document.addEventListener('submit', populate);
})();
Step 6

Configure GTM Triggers

Correct trigger configuration is critical. Using the wrong trigger can cause UTMs to be missed or form fields to be populated before they exist in the DOM.

Tag Trigger Scope
UTM Capture Tag All Pages — Page View Entire site, all traffic
Form Populate Tag DOM Ready Form page only (e.g., /contact)
Step 7

Testing & Verification

Don’t Skip Testing Testing is the most skipped step in UTM implementations. Skipping it means you’ll only discover broken attribution weeks later when your Salesforce data is already polluted.

Test 1 — Capture Works

Visit your site with UTM parameters:

Test URL
https://yoursite.com/contact
  ?utm_source=google
  &utm_medium=cpc
  &utm_campaign=test_campaign
  &utm_term=crm&utm_content=ad1

Open GTM Preview → verify the UTM Capture Tag fired → check that all 5 GTM variables show correct values.

Test 2 — Overwrite Logic

Without clearing sessionStorage, now visit:

Second Visit URL
https://yoursite.com/contact
  ?utm_source=linkedin
  &utm_medium=paid-social

Expected result — old values must be cleared:

Expected State
utm_source   = 'linkedin'    
utm_medium   = 'paid-social' 
utm_campaign = ''            ✓ (cleared)
utm_term     = ''            ✓ (cleared)
utm_content  = ''            ✓ (cleared)

Test 3 — Persistence Across Pages

After landing on a UTM URL, navigate to another page without UTM parameters. Open the browser console and verify:

Browser Console
sessionStorage.getItem('utm_source')   // Should still return 'linkedin'
sessionStorage.getItem('utm_campaign') // Should still return ''

Test 4 — Form Field Population

Navigate to your form page. Open DevTools and run:

Browser Console
document.querySelector('input[name="utm_source"]').value
// Expected: 'linkedin' (or your last UTM source)

Test 5 — End-to-End Salesforce Verification

  1. Submit a test lead using a URL with known UTM parameters
  2. Open the Lead record in Salesforce
  3. Verify all 5 UTM fields are populated with the expected values
  4. Verify fields that were blank in the URL are genuinely empty (not inherited)

Mistakes

Common Mistakes to Avoid

❌ Mistake 1: Partial UTM overwrite

Only updating the UTMs that are present instead of overwriting the entire set. This is the root cause of attribution contamination.

❌ Mistake 2: GTM fires before DOM is ready

Using a Page View trigger for the form populate tag. Use DOM Ready to ensure the hidden fields exist before you try to populate them.

❌ Mistake 3: UTMs not persisting across pages

Recapturing UTMs only on the form page. Capture must happen on all pages because users often land on a blog post or home page first.

❌ Mistake 4: Incorrect Salesforce field API names

Using display names instead of API names. In Salesforce, field API names are case-sensitive and may include a custom suffix like __c.

❌ Mistake 5: Ignoring SPA / dynamic forms

Single-page apps or forms loaded via JavaScript may not be in the DOM when your populate tag fires. Add a fallback listener on form submit events.

Advanced

Advanced Enhancements

Once the basics are working reliably, consider these additional tracking capabilities:

  • First-touch vs. last-touch attribution — store both the first UTM set (in a long-lived cookie) and last UTM set (sessionStorage) to understand the full customer journey
  • Landing page URL — capture window.location.href on first visit to know exactly which page brought the lead in
  • Referrer tracking — store document.referrer to catch organic and direct traffic that doesn’t carry UTM parameters
  • GCLID tracking — capture Google’s click ID (gclid) for offline conversion import back into Google Ads
  • Server-side tracking — use a server-side GTM container or API for more reliable, ad-blocker-resistant attribution
  • Multi-touch attribution models — use tools like HubSpot Attribution or a custom data warehouse to model linear, time-decay, or position-based attribution across touchpoints

Checklist

Summary: The Complete Setup Checklist

  • Add hidden UTM fields to your Salesforce form
  • Create UTM Capture Tag in GTM — fires on All Pages
  • Implement overwrite-all logic (not merge logic)
  • Store in sessionStorage (or cookies for long cycles)
  • Create 5 GTM Custom JavaScript Variables
  • Create Form Populate Tag — fires on DOM Ready, form page only
  • Test capture, overwrite logic, persistence, and form fields
  • Verify end-to-end in Salesforce with a test lead submission
  • Map field API names correctly in Salesforce

UTM tracking isn’t just about capturing parameters — it’s about getting attribution right. The difference between broken and reliable Salesforce data comes down to one rule: always overwrite UTMs as a complete set when a new campaign visit is detected. Do that, and your data becomes something you can actually trust.

Part of the Complete GA4 Audit Guide  ·  Implementation  ·  Configuration  ·  Data Validation  ·  Consent Mode

Quick Fix Request

Tell us about your tracking issue and we’ll get it resolved fast.