Emtiaz
ServicesCase StudiesTestimonialsAboutFAQBlogs
Markting Analytics & Attribution9 min read

First-Touch vs Last-Touch Attribution: Building the Data Layer Schema

Emtiaz Hossain

Author Name

Emtiaz Hossain

Last Edit : Jul 18, 2026, 09:14:00 AM
First-Touch vs Last-Touch Attribution: Building the Data Layer Schema

TABLE OF CONTENTS▼
First-Touch vs Last-Touch Attribution: Building the Data Layer Schema in GTMWhat Is First-Touch vs Last-Touch Attribution?The Attribution JSON SchemaStep 1: cHTML Tag in Web GTMStep 2: JS Variable for Attribution DataStep 3: Pass as GA4 Event ParameterStep 4: Import sGTM Custom TemplateStep 5: Event Data Variables in sGTMStep 6: Create FT/LT VariablesStep 7: Use in TagsCommon IssuesLimitationsConclusion

Contents

0%
First-Touch vs Last-Touch Attribution: Building the Data Layer Schema in GTMWhat Is First-Touch vs Last-Touch Attribution?The Attribution JSON SchemaStep 1: cHTML Tag in Web GTMStep 2: JS Variable for Attribution DataStep 3: Pass as GA4 Event ParameterStep 4: Import sGTM Custom TemplateStep 5: Event Data Variables in sGTMStep 6: Create FT/LT VariablesStep 7: Use in TagsCommon IssuesLimitationsConclusion

First-Touch vs Last-Touch Attribution: Building the Data Layer Schema in GTM

First-touch attribution credits the first channel that brought a user to your site. Last-touch attribution credits the final interaction before conversion. With Google Tag Manager server-side tracking, capture both using localStorage JSON and a custom sGTM custom template.

This GTM attribution schema persists UTM parameters and Google Click IDs (gclid, gbraid, wbraid) across sessions. The first touch vs last touch data flows through GA4 event parameters into server-side GTM variables for Google Ads offline conversions, Google Sheets lead pipelines, and CRM webhooks.

What Is First-Touch vs Last-Touch Attribution?

First-touch assigns 100% credit to the first marketing channel. Last-touch assigns 100% credit to the last channel before conversion. For a useful marketing attribution model, you need both perspectives.

Example: User clicks Google Ad (gclid captured as first touch), leaves, returns 3 days later via organic search (utm_source=google as last touch), then converts. First-touch credits Google Ads. Last-touch credits organic search.

The Attribution JSON Schema

Stored in localStorage under attribution_data. The UTM parameter tracking schema sets first_touch once and updates last_touch on every page visit:

json
{
  "schema_version": 1,
  "first_touch": {
    "gclid": "Cj0K...",
    "gbraid": "",
    "wbraid": "",
    "utm_source": "google",
    "utm_medium": "cpc",
    "utm_campaign": "kitchen_remodel"
  },
  "last_touch": {
    "gclid": "",
    "gbraid": "",
    "wbraid": "",
    "utm_source": "google",
    "utm_medium": "organic",
    "utm_campaign": ""
  },
  "first_touch_timestamp": "2026-07-15 09:23:14",
  "last_touch_timestamp": "2026-07-17 14:05:32"
}

Step 1: cHTML Tag in Web GTM

Tag type: Custom HTML. Name: cHTML - URL Param store in LS. Trigger: Initialization - All Pages. Fires before GA4 loads.

javascript
<script>
(function () {
  var STORAGE_KEY = "attribution_data";
  var storage = {
    schema_version: 1,
    first_touch: {},
    last_touch: {},
    first_touch_timestamp: null,
    last_touch_timestamp: null
  };

  if (!window.location.search || window.location.search.length <= 1) {
    return;
  }

  try {
    var existing = JSON.parse(localStorage.getItem(STORAGE_KEY));
    if (existing) {
      storage = existing;
      if (!storage.schema_version) {
        storage.schema_version = 1;
      }
    }
  } catch (e) {}

  var params = {};
  var query = window.location.search.substring(1);
  var pairs = query.split("&");

  for (var i = 0; i < pairs.length; i++) {
    if (!pairs[i]) continue;
    var pair = pairs[i].split("=");
    var key = decodeURIComponent(pair[0]);
    var value = pair.length > 1
      ? decodeURIComponent(pair.slice(1).join("=").replace(/\+/g, " "))
      : "";
    if (key) params[key] = value;
  }

  if (Object.keys(params).length === 0) return;

  function formatTimestamp(d) {
    return d.getFullYear() + "-" +
      String(d.getMonth() + 1).padStart(2, "0") + "-" +
      String(d.getDate()).padStart(2, "0") + " " +
      String(d.getHours()).padStart(2, "0") + ":" +
      String(d.getMinutes()).padStart(2, "0") + ":" +
      String(d.getSeconds()).padStart(2, "0");
  }

  if (!storage.first_touch_timestamp) {
    storage.first_touch = params;
    storage.first_touch_timestamp = formatTimestamp(new Date());
  }

  storage.last_touch = params;
  storage.last_touch_timestamp = formatTimestamp(new Date());

  localStorage.setItem(STORAGE_KEY, JSON.stringify(storage));
})();
</script>

Step 2: JS Variable for Attribution Data

Create Custom JavaScript variable JS - Attribution Data:

javascript
function() {
  try {
    return JSON.stringify(
      JSON.parse(localStorage.getItem("attribution_data") || "{}")
    );
  } catch(e) {
    return "{}";
  }
}

Reads localStorage and serializes for passing as GA4 custom event parameter.

Step 3: Pass as GA4 Event Parameter

In each GA4 event tag (generate_lead, phone_click, email_click, map_click):

  • ▸Name: custom_properties
  • ▸Value: {{JS - Attribution Data}}

The GA4 event parameters carry the full attribution JSON to server-side GTM. The sGTM GA4 client receives it and makes it available to server-side GTM variables.

Step 4: Import sGTM Custom Template

Download the Attribution JSON Parser from GitHub:

  • ▸https://github.com/EmtiazHossainE2/attribution-json-parser
  • ▸Code > Download ZIP > unzip > Attribution JSON Parser.tpl
  • ▸sGTM: Templates > New > Import > select .tpl > Save

Inputs: jsonInput (serialized JSON) + path (dot-notation like first_touch.gclid)

Step 5: Event Data Variables in sGTM

Create Event Data Variable, keyPath custom_properties, name ED - custom_properties. Feeds the GA4 event parameter into the parser.

Step 6: Create FT/LT Variables

For each field: variable > type Attribution JSON Parser. jsonInput = {{ED - custom_properties}}. Path = field path.

First-touch:

  • ▸FT - gclid
  • ▸FT - gbraid
  • ▸FT - wbraid
  • ▸FT - utm_source
  • ▸FT - utm_medium
  • ▸FT - utm_campaign
  • ▸FT - utm_term
  • ▸FT - utm_content
  • ▸FT - timestamp

Last-touch:

  • ▸LT - gclid
  • ▸LT - gbraid
  • ▸LT - wbraid
  • ▸LT - utm_source
  • ▸LT - utm_medium
  • ▸LT - utm_campaign
  • ▸LT - utm_term
  • ▸LT - utm_content
  • ▸LT - timestamp

Also add schema_version.

Step 7: Use in Tags

  • ▸Google Sheets leads pipeline: map FT/LT attribution to columns for lead tracking per row
  • ▸Google Ads offline conversion: pass gclid and gbraid for offline conversion import and click matching
  • ▸CRM webhooks: include FT/LT data in payloads to Workiz, HubSpot, Salesforce

Common Issues

  • ▸custom_properties not showing: check GA4 tag param key matches exactly
  • ▸Variables return empty: verify ED - custom_properties exists and cHTML fires before GA4 tag
  • ▸First-touch resets: check timestamp logic only sets when null
  • ▸Template import fails: ensure .tpl file. Import from Templates, not Tags.

Limitations

  • ▸Client-side only: localStorage clears or cross-device breaks chain
  • ▸First-touch needs landing page URL params
  • ▸No cross-domain tracking: each domain has its own localStorage

Conclusion

Building FT/LT attribution in GTM gives you both top-of-funnel discovery and bottom-of-funnel conversion visibility. Lightweight approach works with any GA4 property or ad platform supporting URL parameters.

Get the template: https://github.com/EmtiazHossainE2/attribution-json-parser

#GTM#Marketing Attribution#UTM Attribution#JavaScript#sGTM#google analytics 4

Discussed this on LinkedIn

Found this helpful?

Drop a comment on the LinkedIn post – I read every reply and love connecting with tracking folks.

View on LinkedIn

Not Sure If Your Tracking Is Costing You?

Book a free 30-minute call. I will review your current tracking setup, show you where conversions are leaking, and tell you exactly what I would fix.

More Blog Posts

View all
SAG Organic UTM Issue in Shopify → How GMC Infected Attribution Data
Advanced Tracking

SAG Organic UTM Issue in Shopify → How GMC Infected Attribution Data

How Shopify's Google & YouTube channel silently tags free listing URLs with sag_organic, inflating GA4 organic revenue and starving Smart Bidding, and the exact Merchant Center Next fix (Attribute rules, not the old Feed Rules) that stops it at the source.

How I Built a Shopify Attribution Pipeline (UTM & Click IDs → Orders)
Markting Analytics & Attribution

How I Built a Shopify Attribution Pipeline (UTM & Click IDs → Orders)

A real-world case study showing how attribution data (UTM, gclid, fbclid) was captured, persisted, and sent to Shopify orders for accurate marketing attribution. This guide walks through the architecture, implementation, and tracking strategy used in a live TinyBot client project.

Meta CAPI Tracking Using Own Server (Coding Method)
Advanced Tracking

Meta CAPI Tracking Using Own Server (Coding Method)

Learn how to implement Meta Conversions API (CAPI) using your own server in a Next.js project. This guide breaks down the server-side tracking process step-by-step with real code and structure , no third-party tools needed. Perfect for developers and tracking experts who want full control.

Emtiaz
EmailLinkedInPrivacyTerms

© 2026 Emtiaz Hossain