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.
#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>
#Only after consent
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
- Preview the container in Tag Assistant, then publish it.
- 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 }. - In the CLItrail dashboard, open Visits & identities: the visit is listed.
- Anything else names the cause:
disabled(no consent yet, ordata-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 ashttp://localhost:3000, under Settings → Additional domains). IfInstallAttributionis 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.