# The TracerKit data layer (e-commerce event spec)

A standardized, typed e-commerce event schema — push events onto
`window.tracerkitLayer` and TracerKit consumes them for purchase/custom
triggers, verification, and server-side forwarding to ad platforms.
One implementation, every destination.

## The contract

`window.tracerkitLayer` is a plain array with GTM-dataLayer-compatible
push semantics:

```html
<script>
  window.tracerkitLayer = window.tracerkitLayer || [];
</script>
```

Rules:

- window.tracerkitLayer is a plain array with GTM-dataLayer-compatible push semantics. Initialize it with `window.tracerkitLayer = window.tracerkitLayer || []` — order relative to the TracerKit snippet doesn't matter: pushes made before the loader boots are drained on boot; later pushes are processed as they happen.
- Push events at the moment they happen, on the page where they happen. In SPAs, push after the route settles.
- Numbers are JSON numbers. `"129.97"`, `"$129.97"`, and `"1.299,97"` are all invalid — value and price are decimals, quantity is an integer.
- currency is an uppercase ISO 4217 code and is required whenever value is present.
- Push purchase exactly once per order. TracerKit dedupes on order_id, but ad platforms are less forgiving — don't re-push on confirmation-page reloads.
- Unknown extra fields are ignored, so you can share one push with an existing GTM dataLayer implementation.

The shared `Item` shape used by every commerce event:

```ts
type Item = {
  id: string;       // SKU or product ID — stable across events
  name: string;
  price: number;    // unit price, decimal
  quantity: number; // integer ≥ 1
};
```

## Events

Implement all five for a complete funnel; `purchase` alone is the minimum
for revenue verification.

### `page_view`

**When**: On every page load and SPA route change (after the route settles).

| Field | Type | Required | Notes |
| --- | --- | --- | --- |
| `event` | `string` | yes | Always `"page_view"`. |
| `page_location` | `string` | no | Full URL. Defaults to location.href when omitted. |
| `page_title` | `string` | no | Defaults to document.title when omitted. |

```html
<script>
  window.tracerkitLayer = window.tracerkitLayer || [];
  window.tracerkitLayer.push({
    event: "page_view"
  });
</script>
```

### `view_item`

**When**: When a product detail page (or quick-view) renders.

| Field | Type | Required | Notes |
| --- | --- | --- | --- |
| `event` | `string` | yes | Always `"view_item"`. |
| `items` | `Item[]` | yes | The product being viewed — usually a single item. |
| `value` | `number` | no | Price of the viewed item. |
| `currency` | `string` | no | ISO 4217 code, uppercase (USD, EUR, GBP…). Required whenever value is present. |
| `items[].id` | `string` | yes | SKU or product ID. Keep it stable across events so the funnel joins up. |
| `items[].name` | `string` | yes | Human-readable product name. |
| `items[].price` | `number` | yes | Unit price as a decimal number — no currency symbols, never a string. |
| `items[].quantity` | `number` | yes | Integer ≥ 1. |

```html
<script>
  window.tracerkitLayer = window.tracerkitLayer || [];
  window.tracerkitLayer.push({
    event: "view_item",
    value: 39.99,
    currency: "USD",
    items: [{ id: "SKU-123", name: "Canvas Tote", price: 39.99, quantity: 1 }]
  });
</script>
```

### `add_to_cart`

**When**: When an item is added to the cart (not when the cart page opens).

| Field | Type | Required | Notes |
| --- | --- | --- | --- |
| `event` | `string` | yes | Always `"add_to_cart"`. |
| `items` | `Item[]` | yes | The items just added; quantity is the amount added, not the cart total. |
| `value` | `number` | no | Value of the items added (price × quantity). |
| `currency` | `string` | no | ISO 4217 code, uppercase (USD, EUR, GBP…). Required whenever value is present. |
| `items[].id` | `string` | yes | SKU or product ID. Keep it stable across events so the funnel joins up. |
| `items[].name` | `string` | yes | Human-readable product name. |
| `items[].price` | `number` | yes | Unit price as a decimal number — no currency symbols, never a string. |
| `items[].quantity` | `number` | yes | Integer ≥ 1. |

```html
<script>
  window.tracerkitLayer = window.tracerkitLayer || [];
  window.tracerkitLayer.push({
    event: "add_to_cart",
    value: 79.98,
    currency: "USD",
    items: [{ id: "SKU-123", name: "Canvas Tote", price: 39.99, quantity: 2 }]
  });
</script>
```

### `begin_checkout`

**When**: When the customer enters checkout (first checkout step renders).

| Field | Type | Required | Notes |
| --- | --- | --- | --- |
| `event` | `string` | yes | Always `"begin_checkout"`. |
| `items` | `Item[]` | yes | The full cart at checkout start. |
| `value` | `number` | no | Cart total at checkout start. |
| `currency` | `string` | no | ISO 4217 code, uppercase (USD, EUR, GBP…). Required whenever value is present. |
| `items[].id` | `string` | yes | SKU or product ID. Keep it stable across events so the funnel joins up. |
| `items[].name` | `string` | yes | Human-readable product name. |
| `items[].price` | `number` | yes | Unit price as a decimal number — no currency symbols, never a string. |
| `items[].quantity` | `number` | yes | Integer ≥ 1. |

```html
<script>
  window.tracerkitLayer = window.tracerkitLayer || [];
  window.tracerkitLayer.push({
    event: "begin_checkout",
    value: 129.97,
    currency: "USD",
    items: [
      { id: "SKU-123", name: "Canvas Tote", price: 39.99, quantity: 2 },
      { id: "SKU-456", name: "Wool Beanie", price: 49.99, quantity: 1 }
    ]
  });
</script>
```

### `purchase`

**When**: Exactly once per order, on the order-confirmation page.

| Field | Type | Required | Notes |
| --- | --- | --- | --- |
| `event` | `string` | yes | Always `"purchase"`. |
| `order_id` | `string` | yes | Unique per order — the dedup key. Never reuse across orders. |
| `value` | `number` | yes | Order total as a decimal number — never a string, no symbols or separators. |
| `currency` | `string` | yes | ISO 4217 code, uppercase (USD, EUR, GBP…). |
| `items` | `Item[]` | yes | Every line item in the order. |
| `items[].id` | `string` | yes | SKU or product ID. Keep it stable across events so the funnel joins up. |
| `items[].name` | `string` | yes | Human-readable product name. |
| `items[].price` | `number` | yes | Unit price as a decimal number — no currency symbols, never a string. |
| `items[].quantity` | `number` | yes | Integer ≥ 1. |
| `email` | `string` | no | OPTIONAL — enhanced matching only. Hashed in-browser (SHA-256) before use; the raw address never leaves the page. |
| `phone` | `string` | no | OPTIONAL — enhanced matching only. E.164 format recommended (+15551234567). Hashed in-browser (SHA-256) before use. |

```html
<script>
  window.tracerkitLayer = window.tracerkitLayer || [];
  window.tracerkitLayer.push({
    event: "purchase",
    order_id: "1001",
    value: 129.97,
    currency: "USD",
    items: [
      { id: "SKU-123", name: "Canvas Tote", price: 39.99, quantity: 2 },
      { id: "SKU-456", name: "Wool Beanie", price: 49.99, quantity: 1 }
    ],
    email: "jane@example.com",  // optional — hashed in-browser
    phone: "+15551234567"       // optional — hashed in-browser
  });
</script>
```

## Enhanced matching (email & phone)

`email` and `phone` on `purchase` are OPTIONAL. When provided they are
consumed for enhanced matching only: hashed in-browser with SHA-256 before
any transmission — the raw values never leave the page and never transit
TracerKit's servers. Provide them only under a consent state that permits
marketing use.

## Mapping to platforms

Once TracerKit forwards events server-side, the schema maps 1:1 onto each
platform's conversion API — this is why one data layer implementation is
enough.

### Event names

| TracerKit event | Meta CAPI | GA4 | TikTok Events API |
| --- | --- | --- | --- |
| `page_view` | `PageView` | `page_view` | `Pageview` |
| `view_item` | `ViewContent` | `view_item` | `ViewContent` |
| `add_to_cart` | `AddToCart` | `add_to_cart` | `AddToCart` |
| `begin_checkout` | `InitiateCheckout` | `begin_checkout` | `InitiateCheckout` |
| `purchase` | `Purchase` | `purchase` | `CompletePayment` |

### Fields (purchase)

| TracerKit field | Meta CAPI | GA4 | TikTok Events API |
| --- | --- | --- | --- |
| `value` | `custom_data.value` | `params.value` | `properties.value` |
| `currency` | `custom_data.currency` | `params.currency` | `properties.currency` |
| `order_id` | `custom_data.order_id (dedup basis)` | `params.transaction_id` | `properties.order_id` |
| `items[].id` | `custom_data.contents[].id` | `items[].item_id` | `properties.contents[].content_id` |
| `items[].name` | `custom_data.content_name` | `items[].item_name` | `properties.contents[].content_name` |
| `items[].price` | `custom_data.contents[].item_price` | `items[].price` | `properties.contents[].price` |
| `items[].quantity` | `custom_data.contents[].quantity` | `items[].quantity` | `properties.contents[].quantity` |
| `email (hashed)` | `user_data.em` | `user_data.sha256_email_address` | `user.email` |
| `phone (hashed)` | `user_data.ph` | `user_data.sha256_phone_number` | `user.phone` |

## Typed SDK

Prefer typed calls over hand-written pushes? `@tracerkit/js` and
`@tracerkit/react` wrap this spec field-for-field (pushEvent,
pushPurchase, identify, consent): https://tracerkit.com/docs/sdk.md

## Install

The data layer is consumed by the TracerKit loader — install it first:
https://tracerkit.com/docs/install.md

Machine-readable version of this page: https://tracerkit.com/docs/data-layer.md
HTML version: https://tracerkit.com/docs/data-layer
