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

# Web SDK

> Typed tracking SDK for websites and web games. Tracks player progressions, spend, sign-ups, and attribution signals

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

<Info>
  **Who is this for?** Web developers building web games, marketing sites, or SPAs who need explicit event tracking, user identity, or SPA support. Works with any framework (React, Next.js, Svelte, vanilla JS).
</Info>

The Immutable Web SDK is a typed package for tracking player behavior as part of the Immutable attribution system. UTM parameters, click IDs, and referrer data are captured automatically, connecting ad campaigns to the players they drove. Session lifecycle (`session_start`, `session_end`) is handled automatically. Page views are tracked by calling `page()` on each route change, and in-game moments like progressions, purchases, and sign-ups are triggered by your code. All events flow to the same pipeline as the [Tracking Pixel](/docs/products/audience/tracking-pixel), [Unity SDK](/docs/products/audience/unity-sdk), and [REST API](/docs/products/audience/rest-api).

## What You Need

* An [Immutable Hub](https://hub.immutable.com) account with a project ([get started here](/docs/products/hub/getting-started))
* A publishable key from your project settings ([API keys guide](/docs/products/hub/api-keys))

## Installation

<Tabs>
  <Tab title="npm">
    ```bash theme={null}
    npm install @imtbl/audience
    ```
  </Tab>

  <Tab title="yarn">
    ```bash theme={null}
    yarn add @imtbl/audience
    ```
  </Tab>

  <Tab title="pnpm">
    ```bash theme={null}
    pnpm add @imtbl/audience
    ```
  </Tab>

  <Tab title="CDN">
    ```html theme={null}
    <script src="https://cdn.immutable.com/audience/v1/imtbl.js"></script>
    ```

    The CDN bundle attaches `ImmutableAudience` to `window` with the same API surface: `init`, `AudienceEvents`, `IdentityType`, `canTrack`, `canIdentify`, and `version`.

    <Info>
      If your site uses a Content Security Policy, see [Content Security Policy](#content-security-policy) for the directives CDN users need to allow.
    </Info>
  </Tab>
</Tabs>

## Quick Start

<Steps>
  <Step title="Initialize the SDK">
    <Tabs>
      <Tab title="npm">
        ```typescript theme={null}
        import { Audience, AudienceEvents, IdentityType } from '@imtbl/audience';

        const audience = Audience.init({
          publishableKey: 'YOUR_PUBLISHABLE_KEY',
        });
        ```
      </Tab>

      <Tab title="CDN">
        ```html theme={null}
        <script src="https://cdn.immutable.com/audience/v1/imtbl.js"></script>
        <script>
          var audience = ImmutableAudience.init({
            publishableKey: 'YOUR_PUBLISHABLE_KEY',
          });
        </script>
        ```

        Subsequent steps call methods on the `audience` variable. `AudienceEvents` and `IdentityType` are available as `ImmutableAudience.AudienceEvents` and `ImmutableAudience.IdentityType`.
      </Tab>
    </Tabs>

    <Warning>
      Consent defaults to `'none'`. The SDK won't track anything until you explicitly set a consent level.
    </Warning>
  </Step>

  <Step title="Set consent">
    The SDK uses a three-tier consent model (`'none'`, `'anonymous'`, `'full'`). The SDK does not provide a consent UI. Build your own cookie banner or privacy prompt, then call `setConsent` with the appropriate level when the user responds.

    ```typescript theme={null}
    // User accepts anonymous tracking
    audience.setConsent('anonymous');

    // User logs in and accepts full tracking
    audience.setConsent('full');
    ```

    At `'anonymous'`, page views and events are tracked but `identify()` and `alias()` calls are dropped. At `'full'`, all methods are available. See the [Data Dictionary](/docs/products/audience/data-dictionary#consent-model) for what is collected at each level.
  </Step>

  <Step title="Track page views">
    Call `page()` on each route change. The first call in each session includes [attribution](/docs/products/audience/data-dictionary#attribution-signals) parameters.

    ```typescript theme={null}
    audience.page();
    ```
  </Step>

  <Step title="Track events">
    Log player actions with `track()`. The SDK ships with [predefined events](/docs/products/audience/data-dictionary#predefined-events) for common player actions (purchases, sign-ups, progression, wishlist actions, and more) that give you typed properties and autocomplete. You can also pass any custom event string.

    <Tabs>
      <Tab title="npm">
        ```typescript theme={null}
        // Predefined event — typed properties
        audience.track(AudienceEvents.SIGN_UP, { method: 'email' });
        audience.track(AudienceEvents.PURCHASE, { currency: 'USD', value: 9.99 });

        // Custom event — any properties
        audience.track('tutorial_complete', { stepCount: 5 });
        ```
      </Tab>

      <Tab title="CDN">
        ```javascript theme={null}
        // Predefined event — typed properties
        audience.track(ImmutableAudience.AudienceEvents.SIGN_UP, { method: 'email' });
        audience.track(ImmutableAudience.AudienceEvents.PURCHASE, { currency: 'USD', value: 9.99 });

        // Custom event — any properties
        audience.track('tutorial_complete', { stepCount: 5 });
        ```
      </Tab>
    </Tabs>
  </Step>

  <Step title="Identify users">
    Identifies the player by associating their userId with their activity and traits. Call at login or account connection. Requires `'full'` consent.

    <Tabs>
      <Tab title="npm">
        ```typescript theme={null}
        audience.identify('player-123', IdentityType.Passport, {
          email: 'user@example.com',
        });
        ```
      </Tab>

      <Tab title="CDN">
        ```javascript theme={null}
        audience.identify('player-123', ImmutableAudience.IdentityType.Passport, {
          email: 'user@example.com',
        });
        ```
      </Tab>
    </Tabs>
  </Step>

  <Step title="Clean up on exit">
    Flushes the queue and sends a `session_end` event (if consent is above `'none'`).

    ```typescript theme={null}
    audience.shutdown();
    ```
  </Step>
</Steps>

### Complete Example

<Tabs>
  <Tab title="npm">
    ```typescript theme={null}
    import { Audience, AudienceEvents, IdentityType } from '@imtbl/audience';

    // 1. Initialize
    const audience = Audience.init({
      publishableKey: 'YOUR_PUBLISHABLE_KEY',
    });

    // 2. Set consent after cookie banner
    audience.setConsent('anonymous');

    // 3. Track page views on route changes
    audience.page();

    // 4. Track player actions
    audience.track(AudienceEvents.PURCHASE, { currency: 'USD', value: 9.99 });

    // 5. Identify after login (requires 'full' consent)
    audience.setConsent('full');
    audience.identify('player-123', IdentityType.Passport, { email: 'user@example.com' });

    // 6. Clean up on exit
    audience.shutdown();
    ```
  </Tab>

  <Tab title="CDN">
    ```html theme={null}
    <script src="https://cdn.immutable.com/audience/v1/imtbl.js"></script>
    <script>
      // 1. Initialize
      var audience = ImmutableAudience.init({
        publishableKey: 'YOUR_PUBLISHABLE_KEY',
      });

      // 2. Set consent after cookie banner
      audience.setConsent('anonymous');

      // 3. Track page views on route changes
      audience.page();

      // 4. Track player actions
      audience.track(ImmutableAudience.AudienceEvents.PURCHASE, {
        currency: 'USD',
        value: 9.99,
      });

      // 5. Identify after login (requires 'full' consent)
      audience.setConsent('full');
      audience.identify('player-123', ImmutableAudience.IdentityType.Passport, {
        email: 'user@example.com',
      });

      // 6. Clean up on exit
      audience.shutdown();
    </script>
    ```
  </Tab>
</Tabs>

## Verify the Integration

Initialize with `debug: true` to see SDK activity in the browser console:

```typescript theme={null}
const audience = Audience.init({
  publishableKey: 'YOUR_PUBLISHABLE_KEY',
  debug: true,
});
```

1. **Console**: Look for log entries showing `session_start`, `page`, and any `track()` calls
2. **Network tab**: Look for POST requests to `https://api.immutable.com`. Events flush every 5 seconds or when 20 events accumulate

<Info>
  Remove `debug: true` before deploying to production.
</Info>

## Content Security Policy

If your site enforces a Content-Security-Policy header, add the event endpoint so the SDK can deliver events:

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

If you load the SDK from Immutable's CDN, also add the script host:

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

<Info>
  If you install the SDK as a package dependency (npm/yarn/pnpm), only `connect-src` is required. The CDN directive is only needed for the `<script>`-tag installation.
</Info>

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

```html theme={null}
<script src="https://cdn.immutable.com/audience/v1/imtbl.js"></script>
<script nonce="YOUR_NONCE">
  var audience = ImmutableAudience.init({ publishableKey: 'YOUR_PUBLISHABLE_KEY' });
  audience.setConsent('anonymous');
  audience.page();
</script>
```

## API Reference

<AccordionGroup>
  <Accordion title="Audience.init(config)">
    Creates and returns an `Audience` instance. Call once when your app loads to set up tracking.

    | Parameter        | Type                              | Required | Default        | Description                                                                                                     |
    | ---------------- | --------------------------------- | -------- | -------------- | --------------------------------------------------------------------------------------------------------------- |
    | `publishableKey` | `string`                          | Yes      | —              | API key from [Hub](https://hub.immutable.com)                                                                   |
    | `consent`        | `'none' \| 'anonymous' \| 'full'` | No       | `'none'`       | Initial consent level                                                                                           |
    | `debug`          | `boolean`                         | No       | `false`        | Log SDK activity to the browser console                                                                         |
    | `testMode`       | `boolean`                         | No       | `false`        | When `true`, stamps all events with `test: true` so they can be filtered from production analytics.             |
    | `cookieDomain`   | `string`                          | No       | Current domain | Share cookies across subdomains (e.g. `'.studio.com'`). The Tracking Pixel uses `domain` for this same setting. |
    | `flushInterval`  | `number`                          | No       | `5000`         | How often the queue flushes, in milliseconds                                                                    |
    | `flushSize`      | `number`                          | No       | `20`           | Number of queued messages that triggers a flush                                                                 |
    | `baseUrl`        | `string`                          | No       | Immutable API  | Override the default API base URL                                                                               |
    | `onError`        | `(err: AudienceError) => void`    | No       | `undefined`    | Callback for flush and consent sync failures                                                                    |

    ```typescript theme={null}
    const audience = Audience.init({
      publishableKey: 'YOUR_PUBLISHABLE_KEY',
      consent: 'none',
      debug: true,
      cookieDomain: '.studio.com',
      onError: (err) => console.error(err.code, err.message),
    });
    ```
  </Accordion>

  <Accordion title="setConsent(level)">
    Controls what the SDK is allowed to collect. Call when the user accepts or changes their cookie preferences. Takes effect immediately, no page reload needed. See the [Data Dictionary](/docs/products/audience/data-dictionary#consent-model) for consent levels and [downgrade behavior](/docs/products/audience/data-dictionary#downgrading-consent).

    ```typescript theme={null}
    // Upgrade after cookie banner
    audience.setConsent('anonymous');

    // Downgrade — stops tracking, clears cookies
    audience.setConsent('none');
    ```
  </Accordion>

  <Accordion title="canTrack(level)">
    Checks whether a consent level allows tracking. Returns `true` when `level` is `'anonymous'` or `'full'`. Use before calling `track()` or `page()` when you need to guard behavior based on the current consent level, for example when conditionally showing a tracking-dependent UI element.

    | Parameter | Type                              | Description                |
    | --------- | --------------------------------- | -------------------------- |
    | `level`   | `'none' \| 'anonymous' \| 'full'` | The consent level to check |

    ```typescript theme={null}
    import { canTrack } from '@imtbl/audience';

    if (canTrack(currentConsentLevel)) {
      // safe to call track() and page()
    }
    ```
  </Accordion>

  <Accordion title="canIdentify(level)">
    Checks whether a consent level allows player identification. Returns `true` when `level` is `'full'`. Use before calling `identify()` or `alias()` when you need to guard identity-dependent logic, for example to only prompt a player to link accounts when consent allows it.

    | Parameter | Type                              | Description                |
    | --------- | --------------------------------- | -------------------------- |
    | `level`   | `'none' \| 'anonymous' \| 'full'` | The consent level to check |

    ```typescript theme={null}
    import { canIdentify } from '@imtbl/audience';

    if (canIdentify(currentConsentLevel)) {
      // safe to call identify() and alias()
    }
    ```
  </Accordion>

  <Accordion title="page(properties?)">
    Records a page view. Call on every route change to track which pages players visit and in what order. The first call in each session includes [attribution](/docs/products/audience/data-dictionary#attribution-signals) parameters.

    | Parameter    | Type                      | Description                |
    | ------------ | ------------------------- | -------------------------- |
    | `properties` | `Record<string, unknown>` | Optional custom properties |

    **Requires:** `'anonymous'` or `'full'` consent. Calls at `'none'` are silently dropped.
  </Accordion>

  <Accordion title="track(event, properties?)">
    Records a player action. Call when a player does something you want to measure, such as a purchase, sign-up, level completion, or any custom action. [Predefined events](/docs/products/audience/data-dictionary#predefined-events) give you typed properties with autocomplete.

    | Parameter    | Type                      | Description                                                                                                       |
    | ------------ | ------------------------- | ----------------------------------------------------------------------------------------------------------------- |
    | `event`      | `string`                  | An `AudienceEvents` value (e.g. `AudienceEvents.PURCHASE`) or any custom string                                   |
    | `properties` | `Record<string, unknown>` | Event properties. Required for predefined events that have required fields (e.g. `purchase`), optional otherwise. |

    **Requires:** `'anonymous'` or `'full'` consent. Calls at `'none'` are silently dropped.

    ```typescript theme={null}
    // Predefined event — typed properties
    audience.track(AudienceEvents.PURCHASE, { currency: 'USD', value: 9.99, itemName: 'Sword' });

    // Custom event — any properties
    audience.track('tutorial_complete', { stepCount: 5 });
    ```
  </Accordion>

  <Accordion title="identify(id, identityType, traits?)">
    Associates the player's userId with their activity and traits. Call at login or account connection.

    | Parameter      | Type           | Description                                          |
    | -------------- | -------------- | ---------------------------------------------------- |
    | `id`           | `string`       | The player's identifier in that provider             |
    | `identityType` | `IdentityType` | Which provider the ID comes from (see table below)   |
    | `traits`       | `object`       | Optional. `email`, `name`, or custom key-value pairs |

    **Requires:** `'full'` consent.

    ```typescript theme={null}
    audience.identify('player-456', IdentityType.Passport, {
      email: 'user@example.com',
    });
    ```

    **Identity types:**

    | Value        | Provider               |
    | ------------ | ---------------------- |
    | `'passport'` | Immutable Passport     |
    | `'steam'`    | Steam                  |
    | `'epic'`     | Epic Games             |
    | `'google'`   | Google                 |
    | `'apple'`    | Apple                  |
    | `'discord'`  | Discord                |
    | `'email'`    | Email address          |
    | `'custom'`   | Custom identity system |
  </Accordion>

  <Accordion title="alias(from, to)">
    Links two account IDs that belong to the same player. Call when a player connects a second account with a different provider, for example a player previously identified via Steam who later links a Passport account.

    | Parameter | Type                                         | Description               |
    | --------- | -------------------------------------------- | ------------------------- |
    | `from`    | `{ id: string, identityType: IdentityType }` | The account being linked  |
    | `to`      | `{ id: string, identityType: IdentityType }` | The player's main account |

    **Requires:** `'full'` consent.

    ```typescript theme={null}
    audience.alias(
      { id: 'steam-user-789', identityType: IdentityType.Steam },
      { id: 'player-456', identityType: IdentityType.Passport },
    );
    ```
  </Accordion>

  <Accordion title="reset()">
    Wipes the current player's identity and starts a fresh anonymous session. Call when a player logs out so the next player on the same device isn't mixed up.

    ```typescript theme={null}
    audience.reset();
    ```
  </Accordion>

  <Accordion title="flush()">
    Sends all queued events to the server immediately. Call when you need events delivered right now instead of waiting for the next automatic flush. Returns a `Promise` that resolves when the batch is sent.

    ```typescript theme={null}
    await audience.flush();
    ```
  </Accordion>

  <Accordion title="shutdown()">
    Sends any remaining events and shuts down the SDK. Call when the app unmounts or the page is about to unload. Fires a `session_end` event if consent is above `'none'`.
  </Accordion>
</AccordionGroup>

## Event Delivery

* Events are batched and flushed every **5 seconds** or when **20 events** accumulate (configurable via `flushInterval` and `flushSize`)
* On page unload (navigate away, close tab), the queue flushes remaining events so they are not lost

## FAQ

<AccordionGroup>
  <Accordion title="Can I use the Tracking Pixel and Web SDK together?">
    Yes, they're complementary, not alternatives. The [Tracking Pixel](/docs/products/audience/tracking-pixel) handles passive capture (page views, attribution, form submissions, outbound clicks) with no code beyond a snippet. The Web SDK handles explicit events, user identity, and SPA route tracking. Use both on the same site if you need passive and custom tracking. All events flow to the same pipeline. See the [Data Dictionary](/docs/products/audience/data-dictionary) for the full per-event schema.
  </Accordion>

  <Accordion title="What happens if I call track() before consent is set?">
    The call is a no-op. No events are queued or sent. Once you call `setConsent('anonymous')` or higher, subsequent calls will be tracked.
  </Accordion>

  <Accordion title="What browsers are supported?">
    Chrome 80+, Firefox 78+, Safari 14+, Edge 80+.
  </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, call `reset()` after the erasure is accepted. This clears the user's identity, wipes the event queue, and generates a fresh anonymous ID so the next session is not linked to the erased user:

    ```typescript theme={null}
    audience.reset();
    ```
  </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="Tracking Pixel" icon="crosshairs" href="/docs/products/audience/tracking-pixel">
    Passive tracking snippet for marketing sites and landing pages
  </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>
