CLItrail Docs

Add CLItrail to Google Tag Manager

Where the CLItrail tag goes in Google Tag Manager, in its own idiom, with the consent, CSP and client-side routing details and how to check it works.

Written for Google Tag Manager web containers

#Where the tag goes

Edit Tags → New → Custom HTML (trigger: All Pages). Put the tag on every page that shows your install command; site-wide is best, after your existing analytics tags.

<script>
  (function () {
    if (window.InstallAttribution) return;
    var tag = document.createElement('script');
    tag.src = 'https://YOUR_SERVICE/browser.js';
    tag.onload = function () {
      var sdk = window.InstallAttribution;
      sdk.ready = sdk.init({ service: 'https://YOUR_SERVICE', project: 'YOUR_WEBSITE_ID' });
    };
    document.head.appendChild(tag);
  })();
</script>

With Consent Mode, the tag waits for the consent types you require; with a trigger, it fires when your consent manager pushes its event.

Require additional consent for tag to fire: analytics_storage (use ad_storage when installs go to ad platforms), or fire the tag on your consent manager's "consent granted" trigger instead of All Pages.

#Client-side routing

For a site that changes pages without a reload, add a second Custom HTML tag on a History Change trigger:

<script>
  (function () {
    // The first tag recorded the page the browser loaded; this records later page paths.
    var sdk = window.InstallAttribution;
    var entry = performance.getEntriesByType('navigation')[0];
    var loaded = new URL(entry ? entry.name : location.href).pathname;
    if (!sdk || !sdk.ready || location.pathname === loaded || location.pathname === window.clitrailRecorded) return;
    window.clitrailRecorded = location.pathname;
    sdk.ready = sdk.init({ service: 'https://YOUR_SERVICE', project: 'YOUR_WEBSITE_ID' });
  })();
</script>

#Content Security Policy

Custom HTML tags are inline scripts: follow Google's guide to using Tag Manager with a Content Security Policy (a nonce-aware container snippet, or 'unsafe-inline'), and add https://YOUR_SERVICE to script-src and connect-src, and blob: to worker-src (only Safari before 26 needs that one).

script-src 'self' https://YOUR_SERVICE;
connect-src 'self' https://YOUR_SERVICE;
worker-src 'self' blob:;

#Verify it works

  1. Preview the container in Tag Assistant, then publish it.
  2. Open a page with the tag in Chrome or Firefox (accept analytics first if you gate CLItrail on consent), open the developer console and run await InstallAttribution.ready. It resolves to { ok: true, urlId, hasAnalyticsContext, expiresAt }.
  3. In the CLItrail dashboard, open Visits & identities: the visit is listed.
  4. Anything else names the cause: disabled (no consent yet, or data-enabled="false"), opfs_unavailable (the page is not served over https or from localhost), Error (the service refused the visit: add the page's origin, including a development origin such as http://localhost:3000, under Settings → Additional domains). If InstallAttribution is undefined, the tag did not load: check the Network tab and your Content-Security-Policy.

Then run your installer on the same computer and check Install events, or run the hook with --doctor.

#Notes

  • Fire it after your GA4 tag (tag sequencing, or a later trigger) so the SDK can read the GA4 IDs.
  • The snippet uses ES5 syntax, which every GTM container accepts.

Google Tag Manager documentation: support.google.com/tagmanager/answer/6107167

Using other install paths or destinations? The setup generator puts this snippet together with your hooks and destination checklist.