# 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.

```html
<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:

```html
<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`](https://clitrail.com/docs/doctor).

> Tip
> 
> Keep the `init` call: the snippet starts the SDK itself instead of relying on `data-website`, so it does not depend on how GTM adds Custom HTML to the page.

## 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](https://support.google.com/tagmanager/answer/6107167)

Using other install paths or destinations? The [setup generator](https://clitrail.com/docs/setup-generator) puts this snippet together with your hooks and destination checklist.
