# Analytics trackers (/docs/build/analytics-trackers)



Every meaningful user action in an Appbrew app fires an analytics event to all registered trackers: add to cart, purchase, screen view, search. A tracker is a class you ship in your integration package that receives those events and forwards them to your platform.

## The base class [#the-base-class]

Extend `AnalyticsTrackerV2` from `@gauntlet/analytics`. It handles queuing (events fired before your init finishes are replayed after), whitelist filtering, and name mapping. You implement:

* `initTracker(config)` — read your settings, initialize your SDK or client, and declare your whitelists and mappers.
* `sendEvent(event, payload)` — called for every whitelisted event, after filtering and mapping.
* `sendScreenView(screenName)` — called on every screen navigation.
* `setUserDetails(user)` — called when a user signs in or their profile updates (`email`, `phone`, `firstName`, `lastName`).

## A compact tracker [#a-compact-tracker]

```ts title="packages/my-integration/src/tracker.ts"
import { AnalyticsTrackerV2 } from '@gauntlet/analytics'
import {
  AnalyticsEvent,
  AnalyticsEventParams,
  AnalyticsPayload,
  AppConfig,
} from '@gauntlet/types'

export class MyTracker extends AnalyticsTrackerV2 {
  async initTracker(config?: AppConfig) {
    const settings = config?.integrations?.['my-integration']
    if (!settings?.apiKey) return

    // Initialize your SDK or API client here.

    this.eventsWhitelist = [
      AnalyticsEvent.ADD_TO_CART,
      AnalyticsEvent.BEGIN_CHECKOUT,
      AnalyticsEvent.PURCHASE,
      AnalyticsEvent.VIEW_ITEM,
      AnalyticsEvent.SEARCH,
    ]
    this.paramsWhitelist = Object.values(AnalyticsEventParams)

    this.eventsMapper = {
      [AnalyticsEvent.ADD_TO_CART]: 'item_added',
      [AnalyticsEvent.PURCHASE]: 'order_completed',
    }
    this.paramsMapper = {
      item_id: 'product_id',
      item_name: 'product_title',
    }
  }

  async sendEvent(event?: AnalyticsEvent, payload?: AnalyticsPayload) {
    await fetch('https://api.example.com/events', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ event, payload, timestamp: Date.now() }),
    })
  }

  async sendScreenView(screenName?: string) {
    // Optional: forward screen views.
  }

  async setUserDetails(user?: { email?: string; phone?: string }) {
    // Optional: identify the user on your platform.
  }
}
```

<Callout type="error" title="Unset whitelists mean silence">
  Both whitelists default to an **empty list, which is deny-all**. A tracker that never sets `eventsWhitelist` receives no events; one that never sets `paramsWhitelist` receives events with an empty payload. There is no error — your dashboard just stays quiet. Use `Object.values(AnalyticsEvent)` / `Object.values(AnalyticsEventParams)` to opt into everything, or list exactly what you need.
</Callout>

## Whitelists and mappers [#whitelists-and-mappers]

* **`eventsWhitelist`** — the `AnalyticsEvent` values your tracker receives. Everything else is dropped before `sendEvent`.
* **`paramsWhitelist`** — the payload keys forwarded. Keys not listed are stripped.
* **`eventsMapper`** — renames Appbrew event names to your platform's (`PURCHASE` → `order_completed`). Unmapped events keep their original name.
* **`paramsMapper`** — renames payload keys the same way (`item_id` → `product_id`).

The event vocabulary covers the commerce funnel: `VIEW_ITEM`, `VIEW_ITEM_LIST`, `SELECT_ITEM`, `ADD_TO_CART`, `REMOVE_FROM_CART`, `VIEW_CART`, `BEGIN_CHECKOUT`, `PURCHASE` (with `value`, `currency`, `transaction_id`, `items[]`), plus `SEARCH`, `SCREEN_VIEW`, `LOGIN`, `SIGNUP`, wishlist, and coupon events. Events carrying `items[]` describe each line item with `item_id`, `item_name`, `price`, `quantity`, `variant_id`, `sku`, and `handle`.

## Reading your settings [#reading-your-settings]

Your tracker's configuration is whatever merchants entered against your [manifest](/docs/build/integrations) settings, delivered in the app config under your `configKey`:

```ts
const settings = config?.integrations?.['my-integration']
```

Treat every field as optional and bail out of initialization quietly if required settings are missing. An app with your package installed but not configured must not crash or spam errors.

## Registering the tracker [#registering-the-tracker]

The host app adds your tracker during startup, in `src/app/App.tsx`. On 0.27.0 and later it can do that through the [`app` extension host](/docs/build/extending-app-behavior) from `@gauntlet/brewery`; `AnalyticsProvider` does the same job on every version.

<Tabs items="[&#x22;app object (0.27+)&#x22;, &#x22;AnalyticsProvider (all versions)&#x22;]">
  <Tab value="app object (0.27+)">
    ```ts title="src/app/App.tsx"
    import { app } from '@gauntlet/brewery'
    import { MyTracker } from 'my-integration'

    app.trackers.add(new MyTracker())
    ```

    Returns an undo, so an integration that adds a tracker can take it back
    out — see [Extending app behavior](/docs/build/extending-app-behavior).
  </Tab>

  <Tab value="AnalyticsProvider (all versions)">
    ```ts title="src/app/App.tsx"
    import { AnalyticsProvider } from '@gauntlet/analytics'
    import { MyTracker } from 'my-integration'

    AnalyticsProvider.getInstance().addTracker(new MyTracker())
    ```

    This is the form the app scaffold generates today. It works on every
    platform version, 0.27 included.
  </Tab>
</Tabs>

## Verifying events [#verifying-events]

There is no simulator for this. Run the real loop:

1. Run the app with your integration configured (see [Testing your work](/docs/build/testing)).
2. Exercise the flows behind your whitelist: view a product, add to cart, search, complete a purchase.
3. Watch your platform's debugger or live-events view and confirm each action arrives once, under the mapped name, with the payload keys you expect.

If nothing arrives, check the whitelists first, since an unset whitelist is by far the most common cause, then confirm `initTracker` actually ran (missing settings make it return early).
