> ## Documentation Index
> Fetch the complete documentation index at: https://docs.immutable.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Tracking Pixel

> Lightweight JavaScript snippet for marketing sites. Passively captures page views, attribution, and interactions with no SDK installation

<Warning>
  The Tracking Pixel is currently in **alpha**. APIs and behavior may change between releases.
</Warning>

<Info>
  **Who is this for?** Marketers and web developers who want passive tracking on marketing sites, landing pages, or web shops with no package install or build step required.
</Info>

The Immutable Tracking Pixel is a lightweight JavaScript snippet that automatically captures page views, attribution signals, form submissions, and outbound link clicks as part of the Immutable attribution system. Paste it into your site's `<head>` and it starts collecting with no build step or configuration required. For web games and SPAs where you need custom events or user identity, use the [Web SDK](/docs/products/audience/web-sdk). The two work side by side on the same site.

## What You Need

* An [Immutable Hub](https://hub.immutable.com) account with a project ([get started here](/docs/products/hub/getting-started))
* A publishable API key from your project settings ([API keys guide](/docs/products/hub/api-keys))
* Access to the `<head>` tag of the site where you want to install the pixel

## Install the Snippet

Paste this into your site's `<head>` tag:

```html theme={null}
<script>
(function(){
var w=window,i="__imtbl";
w[i]=w[i]||[];
w[i].push(["init",{"key":"YOUR_PUBLISHABLE_KEY","consent":"anonymous"}]);
var s=document.createElement("script");s.async=1;
s.src="https://cdn.immutable.com/pixel/v1/imtbl.js";
document.head.appendChild(s);
})();
</script>
```

Replace `YOUR_PUBLISHABLE_KEY` with your project's publishable key from [Hub](https://hub.immutable.com).

<Info>
  This snippet sets `consent` to `anonymous`, which starts collecting anonymous device signals and page views immediately. If your site requires explicit cookie consent before any tracking, see [Consent Modes](#consent-modes) below.
</Info>

The script loads asynchronously and does not block page rendering. It's under 10 KB gzipped.

## Verify the Integration

Open your site with the snippet installed, then check the browser developer tools:

1. **Network tab**: Look for a request to `https://cdn.immutable.com/pixel/v1/imtbl.js` (the pixel script loading)
2. **Network tab**: Look for POST requests to `https://api.immutable.com` (events being sent). Events flush every 5 seconds or when 20 events accumulate.
3. **Console**: Type `window.__imtbl` to inspect the command queue

If you see the script loading and POST requests firing, the pixel is working.

## Consent Modes

The `consent` option controls what the pixel collects. The default is `none`, meaning the pixel loads but does not collect anything until consent is set. See the [Data Dictionary](/docs/products/audience/data-dictionary#consent-level-matrix) for what is collected at each level.

The pixel does not provide a consent UI. You are responsible for building the cookie banner or privacy prompt and calling `window.__imtbl.push(['consent', level])` when the user makes a choice.

### Setting Consent at Init

To start with the pixel inert (recommended if you have a cookie consent banner):

```html theme={null}
<script>
(function(){
var w=window,i="__imtbl";
w[i]=w[i]||[];
w[i].push(["init",{"key":"YOUR_PUBLISHABLE_KEY"}]);
var s=document.createElement("script");s.async=1;
s.src="https://cdn.immutable.com/pixel/v1/imtbl.js";
document.head.appendChild(s);
})();
</script>
```

Then upgrade consent after the user interacts with your cookie banner:

```javascript theme={null}
// After user accepts analytics cookies
window.__imtbl.push(['consent', 'anonymous']);

// Or after user accepts marketing cookies
window.__imtbl.push(['consent', 'full']);
```

### Automatic CMP Detection

If your site uses a Consent Management Platform (such as OneTrust or Cookiebot), the pixel can detect consent state automatically:

```html theme={null}
<script>
(function(){
var w=window,i="__imtbl";
w[i]=w[i]||[];
w[i].push(["init",{"key":"YOUR_PUBLISHABLE_KEY","consentMode":"auto"}]);
var s=document.createElement("script");s.async=1;
s.src="https://cdn.immutable.com/pixel/v1/imtbl.js";
document.head.appendChild(s);
})();
</script>
```

When `consentMode` is set to `auto`, the pixel starts in `none` and checks for these standards:

1. **Google Consent Mode v2**: reads `analytics_storage` and `ad_storage` from `window.dataLayer`
2. **IAB TCF v2**: reads purpose consents via `window.__tcfapi`

The pixel upgrades consent automatically when the CMP signals it, and continues listening for changes (e.g. when a user updates their cookie preferences).

<Warning>
  `consentMode` and `consent` are mutually exclusive. Do not set both.
</Warning>

### Downgrading Consent

You can [downgrade consent](/docs/products/audience/data-dictionary#downgrading-consent) at any time. Downgrading to `'none'` purges all queued events:

```javascript theme={null}
window.__imtbl.push(['consent', 'none']);
```

## What the Pixel Tracks Automatically

Once consent is set to `anonymous` or higher, the pixel captures these events with no additional code:

| Event                   | When it fires                                       | What it captures                                                                                                                             |
| ----------------------- | --------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
| **Page view**           | Every page load                                     | [Attribution signals](/docs/products/audience/data-dictionary#attribution-signals) (UTMs, click IDs, referrer, landing page, referral codes) |
| **Session start**       | New session begins                                  | Session ID                                                                                                                                   |
| **Session end**         | Page unload                                         | Session ID, duration in seconds                                                                                                              |
| **Form submission**     | HTML form submit                                    | Form action, ID, name, field names. At `full` consent: SHA-256 hashed email.                                                                 |
| **Outbound link click** | Click on external link                              | Destination URL, link text, element ID                                                                                                       |
| **Scroll depth**        | Scroll milestone reached (25%, 50%, 75%, 90%, 100%) | Milestone depth, session ID. Works on standard document scroll and on SPA-style internal scroll containers.                                  |

## Cross-Subdomain Tracking

To track users across subdomains (e.g. `www.example.com` and `shop.example.com`), set the `domain` option:

```javascript theme={null}
window.__imtbl.push(["init",{"key":"YOUR_PUBLISHABLE_KEY","consent":"anonymous","domain":".example.com"}]);
```

## Content Security Policy

If your site enforces a Content-Security-Policy header, add these directives:

```
script-src: https://cdn.immutable.com
connect-src: https://api.immutable.com
```

For nonce-based CSP, add the `nonce` attribute to the inline `<script>` tag. The nonce covers the inline snippet only. The CDN-loaded script is authorized by the `script-src` directive above.

```html theme={null}
<script nonce="YOUR_NONCE">
(function(){
var w=window,i="__imtbl";
w[i]=w[i]||[];
w[i].push(["init",{"key":"YOUR_PUBLISHABLE_KEY","consent":"anonymous"}]);
var s=document.createElement("script");s.async=1;
s.src="https://cdn.immutable.com/pixel/v1/imtbl.js";
document.head.appendChild(s);
})();
</script>
```

## Configuration

### Init Options

Pass these options to the `init` command when initializing the pixel.

| Option               | Type                                  | Default          | Description                                                                                             |
| -------------------- | ------------------------------------- | ---------------- | ------------------------------------------------------------------------------------------------------- |
| `key`                | `string`                              | **(required)**   | Your Immutable publishable API key from [Hub](/docs/products/hub/api-keys)                              |
| `consent`            | `'none'` \| `'anonymous'` \| `'full'` | `'none'`         | Initial consent level                                                                                   |
| `consentMode`        | `'auto'`                              | `undefined`      | Enable automatic CMP detection (Google Consent Mode v2, IAB TCF v2). Mutually exclusive with `consent`. |
| `testMode`           | `boolean`                             | `false`          | When `true`, stamps all events with `test: true` so they can be filtered from production analytics.     |
| `domain`             | `string`                              | Current hostname | Cookie domain scope. Set to `.example.com` for cross-subdomain tracking.                                |
| `autocapture.forms`  | `boolean`                             | `true`           | Auto-capture HTML form submissions                                                                      |
| `autocapture.clicks` | `boolean`                             | `true`           | Auto-capture outbound link clicks                                                                       |
| `autocapture.scroll` | `boolean`                             | `true`           | Auto-capture scroll depth milestones                                                                    |

```html theme={null}
<script>
(function(){
var w=window,i="__imtbl";
w[i]=w[i]||[];
w[i].push(["init",{
  "key":"YOUR_PUBLISHABLE_KEY",
  "consent":"anonymous",
  "domain":".example.com",
  "autocapture":{"forms":true,"clicks":false}
}]);
var s=document.createElement("script");s.async=1;
s.src="https://cdn.immutable.com/pixel/v1/imtbl.js";
document.head.appendChild(s);
})();
</script>
```

### Command Queue

All commands use the `window.__imtbl.push()` pattern. The command queue buffers calls made before the pixel script finishes loading, then replays them in order.

| Command                 | Arguments                             | Description                                                                                       |
| ----------------------- | ------------------------------------- | ------------------------------------------------------------------------------------------------- |
| `['init', options]`     | Init options (see above)              | Initialize the pixel. Must be called exactly once.                                                |
| `['consent', level]`    | `'none'` \| `'anonymous'` \| `'full'` | Update the consent level at runtime. Downgrading to `none` purges PII from the local event queue. |
| `['page', properties?]` | `Record<string, unknown>` (optional)  | Manually fire a page view with optional custom properties.                                        |

The pixel fires a page view automatically on load. Use the `page` command to manually trigger additional page views on SPA route changes. Calling `page` also resets scroll depth milestones so each route starts with a fresh counter:

```javascript theme={null}
window.__imtbl.push(['page', { section: 'pricing' }]);
```

### Auto-Capture

By default, the pixel automatically captures form submissions and outbound link clicks. You can disable either or both:

```javascript theme={null}
// Disable form capture, keep click tracking
window.__imtbl.push(["init",{
  "key":"YOUR_PUBLISHABLE_KEY",
  "consent":"anonymous",
  "autocapture":{"forms":false,"clicks":true}
}]);

// Disable all auto-capture (only page views and sessions)
window.__imtbl.push(["init",{
  "key":"YOUR_PUBLISHABLE_KEY",
  "consent":"anonymous",
  "autocapture":{"forms":false,"clicks":false}
}]);
```

### Event Delivery

* Events are batched and flushed every **5 seconds** or when **20 events** accumulate, whichever comes first
* The `session_end` event uses `navigator.sendBeacon()` to ensure delivery on page unload
* All events include `surface: "pixel"` to distinguish them from Web SDK events in the pipeline

## FAQ

<AccordionGroup>
  <Accordion title="Can I use the Tracking Pixel and Web SDK together?">
    Yes, they're complementary, not alternatives. The Pixel handles passive capture (page views, attribution, form submissions, outbound clicks) with no code beyond the snippet. Add the [Web SDK](/docs/products/audience/web-sdk) when you need custom events, user identity, or SPA route tracking.
  </Accordion>

  <Accordion title="Will ad blockers affect the pixel?">
    The pixel uses first-party cookies and standard `fetch()` requests, which minimizes interference from ad blockers. First-party domain hosting further reduces this risk.
  </Accordion>

  <Accordion title="Does the pixel support Single Page Applications?">
    Partially. The pixel does not detect route changes automatically — call `window.__imtbl.push(['page'])` on each SPA route change to fire a page view. This also resets scroll depth milestones so the new route starts with a fresh counter.

    Scroll depth tracking works on internal scroll containers (the pattern used by Angular, Vue, and React apps) as well as standard document scroll, so no extra configuration is needed there.

    **Cross-origin iframes:** the same-origin policy prevents the pixel from observing scroll inside a cross-origin iframe. If your site loads content this way, install the pixel inside the iframe as well.
  </Accordion>

  <Accordion title="What's the bundle size?">
    Under 10 KB gzipped. The script loads asynchronously and does not block page rendering.
  </Accordion>

  <Accordion title="What browsers are supported?">
    Chrome 80+, Firefox 78+, Safari 14+, Edge 80+.
  </Accordion>

  <Accordion title="How does this work with OneTrust or Cookiebot?">
    Use `consentMode: 'auto'` for automatic detection. Or manually call `window.__imtbl.push(['consent', 'full'])` from your CMP's callback.
  </Accordion>

  <Accordion title="How do I handle a GDPR erasure request?">
    Erasure is a server-side operation. Call `DELETE /v1/audience/data` from your backend with the user's `userId` or `anonymousId`. See [Deleting User Data](/docs/products/audience/rest-api#deleting-user-data) in the REST API docs for the full request shape.

    On the client side, downgrade consent to `none` after the erasure is accepted to stop further collection:

    ```javascript theme={null}
    window.__imtbl.push(['consent', 'none']);
    ```
  </Accordion>
</AccordionGroup>

## Next Steps

<CardGroup cols={2}>
  <Card title="Attribution" icon="route" href="/docs/products/audience/attribution">
    How tracking data powers player attribution and Hub reports
  </Card>

  <Card title="Data Dictionary" icon="book" href="/docs/products/audience/data-dictionary">
    Full reference of event schemas and consent levels
  </Card>

  <Card title="Web SDK" icon="code" href="/docs/products/audience/web-sdk">
    Typed SDK for web games, marketing sites, and SPAs
  </Card>

  <Card title="Unity SDK" icon="unity" href="/docs/products/audience/unity-sdk">
    In-game tracking for Unity desktop builds
  </Card>

  <Card title="REST API" icon="server" href="/docs/products/audience/rest-api">
    Send events from your backend or game server
  </Card>

  <Card title="Conversion Postbacks" icon="share-from-square" href="/docs/products/audience/conversion-postbacks">
    Send attributed conversions back to ad networks to improve campaign optimisation
  </Card>

  <Card title="API Keys" icon="key" href="/docs/products/hub/api-keys">
    Manage your publishable and secret keys
  </Card>
</CardGroup>
