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

# Event types

> Reference for every event_type accepted by the Layers Tracking API, including required fields, optional fields, and server-enriched attributes.

Every event in a `POST /beacon` batch is validated against a schema chosen by its `event_type` field. Events that don't match their schema are dropped from the batch.

## Common fields

These fields are accepted on every event. Server-enriched fields (geo, device, browser, OS) are added automatically — don't send them.

<ParamField body="event_id" type="string" required>
  Unique identifier for the event. Use a ULID or UUID generated client-side. Required for deduplication.
</ParamField>

<ParamField body="event_type" type="string" required>
  The event discriminator. Must be one of the values listed in [Supported event types](#supported-event-types).
</ParamField>

<ParamField body="timestamp" type="string" required>
  ISO 8601 timestamp of when the event occurred (e.g., `"2026-06-01T14:30:00Z"`). Accepted as either an ISO string or a `Date`-parseable value; stored in UTC.
</ParamField>

<ParamField body="session_id" type="string" required>
  Anonymized session identifier for the shopper's visit. Should be stable for the duration of a session and rotate at session expiry.
</ParamField>

<ParamField body="customer_id" type="string">
  Logged-in customer identifier. `null` or omitted for guests.
</ParamField>

<ParamField body="attribution_token" type="string">
  The token returned by Search, Browse, or Blocks. Required to attribute downstream `product_click`, `product_view`, `add_to_cart`, and `block_view` events back to the originating request.
</ParamField>

<ParamField body="shopping_channel" type="string">
  `"web"` or `"app"`. Optional. If omitted, the worker infers it from `User-Agent` and the `X-Requested-With` header.
</ParamField>

<ParamField body="product_id" type="number">
  Shopify product ID. Required for product-scoped events (see below).
</ParamField>

<ParamField body="variant_id" type="number">
  Shopify variant ID.
</ParamField>

<ParamField body="collection_handle" type="string">
  Collection handle. Required for `collection_view`.
</ParamField>

<ParamField body="position" type="number">
  1-indexed position in the result set. Required for `product_click`, `product_impression`, `product_hover`, `product_touch`.
</ParamField>

<ParamField body="marketing_source" type="string">UTM source.</ParamField>
<ParamField body="marketing_medium" type="string">UTM medium.</ParamField>
<ParamField body="marketing_campaign" type="string">UTM campaign.</ParamField>

<ParamField body="contextual_data" type="object">
  Free-form additional context (e.g., A/B test bucket, page template, locale). Defaults to `{}`.
</ParamField>

## Server-enriched fields

Don't set these — the worker fills them in:

| Field              | Source                                                            |
| ------------------ | ----------------------------------------------------------------- |
| `geo_country`      | Cloudflare edge (`cf.country`)                                    |
| `geo_state`        | Cloudflare edge (`cf.regionCode`)                                 |
| `geo_city`         | Cloudflare edge (`cf.city`), lowercased                           |
| `device_type`      | Parsed from `User-Agent`                                          |
| `browser`          | Parsed from `User-Agent`                                          |
| `os`               | Parsed from `User-Agent`                                          |
| `shopping_channel` | Inferred from `User-Agent` and `X-Requested-With` if not supplied |

## Supported event types

Engagement events (fired from the storefront):

| `event_type`         | Additional required fields | Description                                   |
| -------------------- | -------------------------- | --------------------------------------------- |
| `collection_view`    | `collection_handle`        | Shopper viewed a collection page.             |
| `product_view`       | `product_id`               | Shopper viewed a product detail page.         |
| `product_click`      | `product_id`, `position`   | Shopper clicked a product in a result list.   |
| `product_impression` | `product_id`, `position`   | A product tile entered the viewport.          |
| `product_hover`      | `product_id`, `position`   | Desktop hover on a product tile.              |
| `product_touch`      | `product_id`, `position`   | Mobile tap or long-press on a product tile.   |
| `add_to_cart`        | `product_id`, `variant_id` | Shopper added a variant to cart.              |
| `search`             | `search_query`             | A text search was executed.                   |
| `autocomplete`       | `search_query`             | An autocomplete query was issued.             |
| `block_view`         | `block_id`                 | A block recommendation widget became visible. |

Request events (typically sent server-side by the platform, not the storefront — included here for completeness):

| `event_type`     | Required fields                                                                       | Description                                     |
| ---------------- | ------------------------------------------------------------------------------------- | ----------------------------------------------- |
| `search_request` | —                                                                                     | A text/image/similar search request was served. |
| `browse_request` | `collection_handle`                                                                   | A collection browse request was served.         |
| `block_request`  | `block_id`, `anchor_type`, `strategy_type`, `num_results`, `current_page`, `per_page` | A block request was served.                     |

<Note>
  Storefront integrations should generally only send the **engagement events** in the first table. The request events are emitted by the Layers platform itself.
</Note>

## Validation rules

* `event_id`, `event_type`, `timestamp`, `session_id` are required on every event.
* `event_type` must be one of the values above. Unknown types are dropped.
* Type-specific required fields above must be present; missing them causes the individual event to be dropped (the batch still ingests).
* The batch must contain between 1 and 100 events. Outside that range, the entire request fails with `422`.

## Next steps

* [Payload examples](/tracking-api/payload-examples) — full request bodies for common scenarios.
* [Send events](/tracking-api/send-events) — endpoint reference.
