First-Party ClickID Attribution with Google Tag Manager

Modern AI tools and LLM-based agents rely heavily on user context. To deliver personalization, automation, and full-funnel attribution, these systems often require their own first-party identifiers - even when your team has no dedicated developers you sometimes have to implement.

This quick guide shows how to deploy a first-party click_id using Google Tag Manager. It works on any site (including SPAs), requires no coding skills, and supports downstream systems like InsightArc, email tools, or custom backend logic - all while staying privacy-first.


Requirements

  • Google Tag Manager is installed on your website.
  • You have “Edit” access to the GTM container.
  • (Optional) Tag Assistant extension in Chrome for debugging.

Step 1: Generate and Persist ClickID

This script creates a unique ID per visitor and stores it in both localStorage and a first-party cookie.

  1. In GTM, go to Tags → New
  2. Name it: [InsightArc] Generate & Persist ClickID
  3. Tag type: Custom HTML
  4. Paste the code:
    <script>
    (function () {
      var KEY = 'click_id';
      var cid = localStorage.getItem(KEY);
      if (!cid) {
     cid = (Date.now()).toString(36) + Math.random().toString(36).substr(2);
     localStorage.setItem(KEY, cid);
     document.cookie = KEY + '=' + cid + ';path=/;SameSite=Lax;Secure';
      }
      window.__CLICK_ID__ = cid;
    })();
    </script>
    

After that Trigger: Page View – All Pages And turn on Save and Publish

To verify, open the browser console and run the command (not in filter, run as command):

window.__CLICK_ID__

This script adds the click_id to all outbound links and supports SPAs like React, Vue, or Gatsby.

Create a new tag: [InsightArc] Append ClickID to Links

Tag type: Custom HTML

Paste the code:

(function(){
  // 1) History API, to dispatch locationchange
  (function(history){
    var push = history.pushState;
    history.pushState = function(){
      var ret = push.apply(this, arguments);
      window.dispatchEvent(new Event('locationchange'));
      return ret;
    };
    window.addEventListener('popstate', function(){
      window.dispatchEvent(new Event('locationchange'));
    });
  })(window.history);

  // 2) Function checks all <a> and adds click_id
  function rewriteLinks(){
    var KEY = 'click_id';
    var cid = window.__CLICK_ID__ || localStorage.getItem(KEY);
    if (!cid) return;
    var as = document.getElementsByTagName('a');
    for (var i = 0; i < as.length; i++) {
      var a = as[i];
      var href = a.getAttribute('href');
      if (!href || href.indexOf('javascript:') === 0) continue;
      var parts  = href.split('?'),
          base   = parts[0],
          qs     = parts[1] || '',
          params = qs ? qs.split('&') : [],
          found  = false;
      for (var j = 0; j < params.length; j++) {
        if (params[j].indexOf(KEY + '=') === 0) {
          params[j] = KEY + '=' + cid;
          found = true;
          break;
        }
      }
      if (!found) params.push(KEY + '=' + cid);
      a.setAttribute('href', base + '?' + params.join('&'));
    }
  }

  // 3) First launch
  rewriteLinks();

  // 4) Repeat while SPA-navigation
  window.addEventListener('locationchange', rewriteLinks);

  // 5) If case of dynamic embeddings (filters inside the page etc.)
  var mo = new MutationObserver(function(muts){
    muts.forEach(function(m){
      if (m.addedNodes.length) rewriteLinks();
    });
  });
  mo.observe(document.body, { childList: true, subtree: true });

})();
</script>

Add all three triggers:

  • Page View – All Pages

  • DOM Ready

  • History Change (built-in trigger for SPA navigation)

Save and publish.

To test it, open browser DevTools and run:

$$('a[href*="click_id="]')

Step 4: Testing and Validation

Make sure everything works as expected.

Check the value Open console:

window.__CLICK_ID__

Should return a valid string.

###Check the links Open console:

$$('a[href*="click_id="]')

Click through internal links. New links should still contain click_id.