> ## Documentation Index
> Fetch the complete documentation index at: https://client-integrations-api-dev.sequen.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Web Pixel

> Send shopper events from your website to Sequen with a script tag, without a backend integration.

The Sequen pixel is a small script (about 6 KB compressed) that sends shopper events from your website straight to Sequen. Use it when you can't send [events](https://client-integrations-api-dev.sequen.ai/api-reference/events.md) from your backend, or when you don't store event history yourself.

Sequen uses these events to rank products for each shopper, including within the current session. The events that matter most are page views, searches, impressions (what was shown, and where), clicks, add to cart and purchases.

| Host | What it serves |
| - | - |
| `https://s.sequen.com/s.js` | The pixel script, from a global CDN |
| `https://c.sequen.com` | Where events are sent |

The pixel needs no API key. It carries a public **pixel id** that identifies your site.

## Before you start

You need three things in place: a pixel id, your origins registered with Sequen, and your Content-Security-Policy updated.

* **Pixel id.** Six characters, like `fK53sD`. It is public, not a secret: it goes in your page code. Your Sequen account team creates it for you.
* **Origins.** Tell Sequen every origin that will send events, including staging. Events from any other origin are refused. Origins match exactly, including the port.
  * An exact origin: `https://www.example.com`
  * Every subdomain: `https://*.example.com`
  * Local testing: `http://localhost:<port>`, with the port you test on
* **Content-Security-Policy.** If your site sends a CSP header, add:

| Directive | Add | Why |
| - | - | - |
| `script-src` | `https://s.sequen.com` | Loads the pixel script |
| `connect-src` | `https://c.sequen.com` | Sends events |
| `img-src` | `https://c.sequen.com` | [Image fallback](#image-fallback) only |

> [!TIP]
>
> You don't need a pixel id to start building. Use any id with `debug: true` and the pixel logs every event to the browser console without sending it. See [Test, then go live](#test-then-go-live).

## Install

Put this snippet in the `<head>` of every page, with your pixel id:

```html theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
<script>
  (function (w, d, u) {
    w.sequen = w.sequen || function () { (w.sequen.q = w.sequen.q || []).push([arguments, +new Date()]); };
    var s = d.createElement("script"); s.async = 1; s.src = u; d.head.appendChild(s);
  })(window, document, "https://s.sequen.com/s.js");

  sequen("init", "<PIXEL_ID>", { itemIdRelation: "products" });
</script>
```

* **Catalog table.** `itemIdRelation` names the [catalog](https://client-integrations-api-dev.sequen.ai/api-reference/catalog.md) table your item ids come from, such as `products`. Every event with an item id carries it as `item_id_relation`.
* **Session and customer ids.** If your site calls [Recommendations](https://client-integrations-api-dev.sequen.ai/api-reference/recommendations.md) with a `session_id`, pass that same id to `init` as `sessionId`, and your customer id as `userId`. See [Shopper ids](#shopper-ids).
* **Asynchronous.** The script loads without blocking the page. Calls made before it loads are queued, and each keeps the time it was made.
* **Automatic page view.** `init` sends a `page_view` when the pixel loads. If you send your own page views with `sequen("page", …)`, put that call in the same snippet, or set `autoPage: false`.
* **Sent quickly.** Events are sent within about 100 ms, so Sequen can personalize later pages in the same session. When the shopper leaves the page, anything left is sent with a `keepalive` request.

Using a tag manager such as Adobe Tags or Google Tag Manager? See [Tag managers](https://client-integrations-api-dev.sequen.ai/pixel/tag-managers.md).

## Page views

Send one page view on every page. `page_type` is required. Use a small fixed set, such as `home`, `category`, `search`, `product_detail`, `cart`, `checkout` and `other`, and share your list with Sequen.

```js theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
sequen("page", { page_type: "category" });
```

A product page passes its item id:

```js theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
sequen("page", { page_type: "product_detail", item_id: "SKU-123" });
```

A search results page passes the query and the results, in the order shown:

```js theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
sequen("page", { page_type: "search", query: "chicken feeder", item_ids: ["SKU-1", "SKU-2", "SKU-3"] });
```

If you don't pass `page_type`, the pixel uses the `pageType` init option, then `<meta name="sqn:page_type" content="…">` on the page, then `other`.

**Single-page apps.** Set `spa: true` in `init` and the pixel sends a page view on every `history.pushState` and back or forward navigation. Or send `sequen("page", …)` yourself on every route change, which lets you pass `page_type` and item ids.

## Impressions and clicks

Impressions tell Sequen what each shopper saw, and where. Ranking models need what was shown as well as what was clicked, so these are the events Sequen most needs. You can send them with markup or with calls, but not both for the same events: each would be sent twice.

### With markup (recommended)

Mark up each grid, carousel or ad slot, and each product tile in it. The pixel records impressions, module views and clicks on its own:

```html theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
<section data-sqn-module="search_results" data-sqn-rec-id="…">
  <div data-sqn-item="SKU-123">
    <a href="/p/SKU-123">Chicken feeder</a>
    <button data-sqn-click="product_favorite">♡</button>
    <button data-sqn-conversion="add_to_cart">Add to cart</button>
  </div>
</section>
```

| Attribute | Goes on | Meaning |
| - | - | - |
| `data-sqn-module` | A grid, carousel or ad slot | The module's name, such as `search_results` or `also_bought` |
| `data-sqn-item` | A product tile: the element that holds its link and buttons | The item id, sent as `item_id`. A click anywhere in the tile, other than on a `data-sqn-click` or `data-sqn-conversion` button, is a `product_click` |
| `data-sqn-click` | A button in a tile | A click with this `click_type`, such as `product_favorite` |
| `data-sqn-conversion` | A button in a tile | A conversion with this `conversion_type`, such as `add_to_cart` |
| `data-sqn-rec-id` | A module | The `rec_id` of the Sequen response that filled the module |
| `data-sqn-rank` | A tile or module | Overrides the position the pixel works out |

* **Impression.** Sent when at least 50% of a tile is in view for 1 second, once per tile per page view.
* **Module view.** Sent just before the module's first impression.
* **Rank.** For a tile, its position within the module, starting at 1. For a module, its position on the page.
* **Clicks link to impressions.** A click on a tile that hasn't logged an impression yet logs the impression first.
* **Late content.** Tiles added after the page loads are picked up, so infinite scroll and lazy-loaded carousels work.

### With calls

If you can't change the page markup, send one impression per product shown. `rank` is required: the product's position in that module, starting at 1.

```js theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
sequen("track", "impression", { item_id: "SKU-123", rank: 3, module_name: "search_results" });
```

Send a click when a shopper opens a product from a module, with the same `rank` and `module_name` as its impression. `click_type` is required:

```js theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
sequen("track", "click", { click_type: "product_click", item_id: "SKU-123", rank: 3, module_name: "search_results" });
```

Adding to a wishlist is a click with `click_type: "product_favorite"`. When a Sequen response filled the module, pass its `rec_id` on both events.

## Add to cart and purchase

```js theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
sequen("conversion", "add_to_cart", { items: [{ item_id: "SKU-123", quantity: 1, price: 24.99 }] });

sequen("conversion", "purchase", {
  order_id: "ORD-1001",
  revenue: 74.97,
  currency: "USD",
  items: [{ item_id: "SKU-123", quantity: 3, price: 24.99 }],
});
```

`item_ids` is filled in from `items`, so you don't need to send both.

## Shopper ids

Every event carries up to three ids:

| Field | Meaning | Where it comes from |
| - | - | - |
| `cid` | The device | A first-party cookie the pixel sets |
| `sid` | The session | The pixel's own session, which rolls over after 30 minutes idle, or your `sessionId` |
| `uid` | The logged-in shopper | `sequen("identify", …)`, or your `userId` |

`sid` and `uid` are the `session_id` and `user_id` of your [Recommendations](https://client-integrations-api-dev.sequen.ai/api-reference/recommendations.md) requests. Your page can't read the pixel's own session id, so if you send `session_id` to Recommendations, pass the same id to `init` as `sessionId` (see **Your own ids** below).

**Logins and logouts.** Call `identify` when a shopper logs in and `reset` when they log out. `reset` clears `uid` and starts a new session.

```js theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
sequen("identify", "<customer id>");
sequen("reset");
```

**Your own ids.** If your site already has session and customer ids, pass them to `init`, and the pixel uses them instead of its own:

```js theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
sequen("init", "<PIXEL_ID>", { sessionId: "<your session id>", userId: "<customer id, or empty>" });
```

* **`sessionId`** is sent as `sid`, and the pixel keeps no session of its own. If it is empty, the pixel falls back to its own session.
* **`userId`** is sent as `uid`. It is never stored, so pass it on every page, and `""` when the shopper is logged out.

> [!WARNING]
>
> Use the same customer and session ids everywhere you send them to Sequen, including your [Recommendations](https://client-integrations-api-dev.sequen.ai/api-reference/recommendations.md) requests, so events and requests can be matched. Send the raw customer id, not a hash, and never an email address or phone number.

## Consent

If your consent manager must approve tracking first, start the pixel with `consent: "pending"`. Nothing is stored or sent until consent is granted:

```js theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
sequen("init", "<PIXEL_ID>", { consent: "pending" });

// Later, from your consent manager's callback:
sequen("consent", "granted");   // sends what was held, and continues
sequen("consent", "denied");    // drops what was held and clears the pixel's ids
```

Any value other than `"granted"` counts as denied. A `consent` call made before the pixel loads wins over the `init` option.

To store nothing in the browser at all, neither cookies nor localStorage, set `cookies: false`. `cid` is then new on every page load, so pair it with `sessionId` and `userId`.

## Custom events

Send any other shopper action with `track`:

```js theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
sequen("track", "video_play", { item_id: "SKU-123", position_sec: 0 });
```

> [!WARNING]
>
> Tell your Sequen account team which custom event types you'll send before you send them, so they can be enabled for your pixel. Events of a type that isn't enabled are dropped. Debug mode doesn't check this.

## Event fields

The pixel sends the same events as the [Events API](https://client-integrations-api-dev.sequen.ai/api-reference/events.md), and follows its rules.

### Event types

| Type | Sent by | Required field |
| - | - | - |
| `page_view` | `sequen("page", …)`, or on its own | `page_type` |
| `module_view` | Markup | `rank` |
| `impression` | Markup, or `sequen("track", "impression", …)` | `rank` |
| `click` | Markup, or `sequen("track", "click", …)` | `click_type` |
| `conversion` | Markup, or `sequen("conversion", …)` | `conversion_type` |
| `dwell_time` | On its own | `dwell_ms` |
| `identify` | `sequen("identify", …)` | None |

An event without its required field is dropped. Any other type is a [custom event](#custom-events), and is dropped unless it is enabled for your pixel.

### Standard fields

| Field | Type | Meaning |
| - | - | - |
| `item_id` | string | The one item an event is about: a product page, an impression, a click |
| `item_ids` | array of strings | A list of items: search results, or the items in a conversion |
| `item_id_relation` | string | The catalog table the item ids come from. Set it once with `itemIdRelation` |
| `rank` | integer | A position, starting at 1 |
| `rec_id` | string | The `rec_id` of the Sequen response that filled the module |
| `page_type`, `click_type`, `conversion_type` | string | The kind of page, click or conversion |
| `dwell_ms` | integer | Time on the page, in milliseconds |
| `page_view_id`, `module_view_id`, `impression_id` | string | Link an event to its page view, module view and impression. The pixel fills in `page_view_id` on every event, and markup fills in the others |

Use `item_id` when an event is about one product, and `item_ids` only for a list. Send item ids as strings.

### Fields Sequen sets

The pixel sets `cid`, `sid`, `uid` (see [Shopper ids](#shopper-ids)), `page_view_id` and `event_id`, and records when each call was made, corrected for the browser's clock. Sequen ignores any value you send for `id`, `client_id`, `original_timestamp`, `server_timestamp`, `ingest_timestamp`, `ip_address` and `user_agent`, and adds a few fields of its own whose names start with `sqn_`.

### Custom fields

Any other field is a custom field and is kept as you send it: a string, number, boolean, array of strings or object. Send as many as you have. Sequen picks the ones that predict what shoppers do.

* **Names.** Letters, digits, `_`, `.`, `:` and `-`, up to 64 characters. Fields with any other name are dropped.
* **Size.** An event can be up to 16 KB. A larger one is dropped.

## Image fallback

Use an image tag only where JavaScript can't run. Each image sends one event, and the response is a 1×1 GIF.

```html theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
<img src="https://c.sequen.com/i?id=<PIXEL_ID>&ev=page_view&page_type=product_detail&item_id=SKU-123&item_id_relation=products&sid=<session id>&uid=<customer id>&r=<random>"
     width="1" height="1" alt="" style="display:none">
```

| Parameter | Required | Meaning |
| - | - | - |
| `id` | Yes | Your pixel id |
| `ev` | No | The event type. Defaults to `page_view`, whose `page_type` then defaults to `other` |
| `sid`, `uid` | At least one | Session and customer ids. Without JavaScript there is no device cookie, so these are the only ids |
| `item_id` | No | One item id |
| `item_ids` | No | A list: repeat the parameter, or separate values with commas |
| `item_id_relation` | No | The catalog table the item ids come from, as `itemIdRelation` |
| `rank`, `dwell_ms` | No | Numbers |
| `r` | No | A random cache-buster, ignored. A browser won't request the same image URL twice on one page |
| Any other | No | Sent as a text field |

* The event time is when the request arrives.
* Your origin is checked from the `Referer` header. Don't set `referrerpolicy="no-referrer"` on the tag or the page, or the event is refused.

## Test, then go live

### Check your events in debug mode

Add `debug: true` to the `init` options. The pixel logs each payload to the browser console and sends nothing:

```js theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
sequen("init", "<PIXEL_ID>", { debug: true });
```

Browse your site and watch the console. Each request shows as `[sequen] POST https://c.sequen.com/e {…}` with its events, or `[sequen] beacon …` when you leave a page (turn on **Preserve log** in DevTools to keep those). Check that each event has the fields you expect, and that `sid` and `uid` are the ids you expect.

### Send for real

Remove `debug`, then check the Network tab in DevTools for requests to `c.sequen.com/e`:

| Status | Meaning |
| - | - |
| `204` | Received |
| `401` | Unknown pixel id |
| `403` | This origin isn't registered with Sequen |
| `413` | The request was too large |
| `429` | Too many requests from one IP address |
| `503` | Sequen is briefly unavailable. The pixel keeps the events and sends them from the next page |

A `204` doesn't mean every event was kept. Sequen drops, on its own:

* Events from bots and automated browsers (headless Chrome, Puppeteer, Playwright, Selenium, uptime monitors), so test in a normal browser.
* Events of a [custom type](#custom-events) that isn't enabled for your pixel.
* Events without their [required field](#event-types).

### Tell Sequen

Tell your Sequen account team when the pixel is live, and we'll confirm the events are arriving.

## Reference

### Calls

| Call | Sends |
| - | - |
| `sequen("init", pixelId, options)` | Starts the pixel, and sends a page view unless `autoPage: false`. Call it once per page, before the others |
| `sequen("page", props)` | `page_view`. `page_type` is required |
| `sequen("track", type, props)` | An event of that type, such as `impression` or `click`, or a [custom event](#custom-events) |
| `sequen("conversion", type, props)` | `conversion`, such as `add_to_cart` or `purchase` |
| `sequen("identify", customerId)` | Sets the customer id after a login |
| `sequen("reset")` | Clears the customer id and starts a new session, after a logout |
| `sequen("consent", "granted" \| "denied")` | Grants or denies consent. See [Consent](#consent) |

### Init options

| Option | Default | What it does |
| - | - | - |
| `autoPage` | `true` | Sends a page view when the pixel starts |
| `itemIdRelation` | none | The catalog table your item ids come from, sent as `item_id_relation` on every event with `item_id` or `item_ids` |
| `pageType` | none | `page_type` for page views that don't pass one: a string, or a function of the page URL |
| `spa` | `false` | Sends a page view on `history.pushState` and back or forward navigation |
| `sessionId` | none | Your session id, sent as `sid`. Empty: the pixel keeps its own 30-minute session |
| `userId` | none | Your customer id, sent as `uid`. Pass it on every page, `""` when logged out |
| `consent` | `"granted"` | `"pending"` holds events in memory and writes no cookies until consent is granted |
| `cookies` | `true` | `false` stores nothing in the browser. `cid` is then new on every page load, and events that fail to send aren't retried on the next page |
| `cookieDomain` | The widest domain that accepts it | Cookie domain for the device id |
| `autocapture` | `true` | Records impressions, module views and clicks from `data-sqn-*` markup |
| `impressionMs` | `1000` | How long a tile must be in view to count as an impression, in ms |
| `impressionRatio` | `0.5` | How much of a tile must be in view |
| `flushMs` | `100` | Events fired within this many ms share one request |
| `debug` | `false` | Logs each payload to the console and sends nothing |

### What the pixel sends on its own

| Event | When |
| - | - |
| `page_view` | When the pixel starts, unless `autoPage: false`; on route changes with `spa: true` |
| `impression`, `module_view`, `click`, `conversion` | From `data-sqn-*` markup |
| `dwell_time` | When the shopper hides or leaves the page, with `dwell_ms` and `max_scroll_pct`. A page hidden twice sends two, with growing totals |

### What every event carries

| Field | Meaning |
| - | - |
| `cid` | The device id |
| `sid` | The session id: your `sessionId`, or the pixel's own |
| `uid` | The customer id, when known |
| `page_view_id` | The page view the event belongs to |
| `event_id` | A unique id per event. A retried send keeps the same id |

By default the pixel keeps the device id in the first-party cookie `_sqn_c`, its own session in `_sqn_s`, and the id from `identify` in `_sqn_u`.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.