# Tracking script

> Send workspace and custom events from your website with the Emailit tracking script and a public tracking key. Install it, track events, identify visitors and limit the key to your sites.

The tracking script sends events from your website: product views, sign-ups, items added to a cart. It needs no setup call; the tracking key is part of its URL. This page explains how to create a key, add the script, track events and tie them to your visitors.

## Before you begin

- The tracking script is included with Pro, Business and Custom plans. On Pay as you go you can't create tracking keys, and the script's requests return `403`, also with keys created before a move to Pay as you go.
- To create tracking keys in the dashboard, you need the **Admin** role in the workspace. With the API, use a key with **Full Access**.
- You need to be able to edit your site's HTML, or add a custom HTML tag in your tag manager.

## Create a tracking key

Tracking keys are public: they go into your web pages, so anyone can read them. They can only send workspace and custom events, never read data, and you can limit them to your own sites.

**Dashboard**

  1. **Open Tracking.** Go to **Email API → Events**, select the **Tracking** tab, then **Create tracking key**.

  2. **Name the key.** Enter a **Name** that says where it's used, such as `Online shop`.

  3. **Limit it to your sites.** Under **Allowed origins**, enter your sites one per line, for example `https://shop.example.com` or `*.example.com`. Leave it empty to accept any site. See [Allowed sites](#allowed-sites).

  4. **Create it.** Select **Create**. The **Tracking** tab now shows the snippet with your key filled in.

**API**

  Call [Create a tracking key](/docs/api-reference/tracking-keys/create/). The `key` in the response goes into the script URL.

```bash
curl -X POST https://api.emailit.com/v2/tracking-keys \
  -H "Authorization: Bearer $EMAILIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Online shop", "allowed_origins": ["https://shop.example.com"] }'
```

## Add the script

Paste both tags into every page, ideally before `</head>`, with your key in place of `TRACKING_KEY`:

```html
<script>window.emailit=window.emailit||function(){(window._emailitq=window._emailitq||[]).push(arguments)};</script>
<script async src="https://js.emailit.com/track/TRACKING_KEY/events.js"></script>
```

The first tag queues calls made before the script loads, so you can call `emailit(...)` right away. The script loads asynchronously, never throws into your page and also works from a tag manager.

- If the Emailit [forms script](/docs/forms/install/) is on the page too, keep both. They share `window.emailit`.
- If your site uses a Content Security Policy, allow `https://js.emailit.com` in `script-src` and `connect-src`.

## Track events

```javascript
// Custom event: any name after "c:", any properties
emailit("track", "c:product.viewed", { sku: "TS-42", price: 19.9 });

// Workspace event: checked against its definition
emailit("track", "w:customer.type", { segment: "b2b" });
```

The type must start with `c:` or `w:`; the script ignores other calls. Properties that aren't an object are sent as `{}`. You can pass a third argument with `occurred_at`, an ISO 8601 date-time, for events that happened earlier.

The script doesn't record page views by itself. Track the pages and actions you care about, for example a `c:page.viewed` event on each route change of a single-page app.

### Shop commands

For online shops, the script also has `product`, `cart`, `purchase` and `page` commands. They record product views, carts and orders for [Commerce](/docs/commerce/) and send them to the event log as the system events `product.viewed`, `cart.updated` and `order.placed`. Commerce is in alpha and open to the Emailit team only; elsewhere these commands are rejected. See [Connect your shop](/docs/commerce/connect-your-shop/#record-shop-activity).

## Identify visitors

Once you know who the visitor is, for example after they sign in or subscribe, tell the script:

```javascript
emailit("identify", { email: "jane@example.com" });
// or, if you store Emailit contact IDs:
emailit("identify", { contact_id: "con_2wPq7Lm3XcN8bV1zKd4RtY6sHf9" });
```

Every later event carries that email or contact ID, on this page and on later visits, so Emailit can tie it to the contact. When the visitor signs out, forget them:

```javascript
emailit("reset");
```

`reset` also starts a new anonymous visitor ID, so the next person on a shared computer isn't mixed up with the last one.

Events with an email that no contact has are kept, and they show on the contact once it exists. A `contact_id` that doesn't match a contact is dropped from the event, without an error, so a public key can't be used to test which contact IDs exist.

Anyone with your tracking key can call `identify` with any address, so Emailit doesn't let the script's events drive automations. They're recorded, show on the contact and reach webhooks, but don't start, end or advance [automation runs](/docs/automations/triggers/#custom-event). Send events that should do that from your server with the [Events API](/docs/events/send-events/). Each event from the script that names a contact has `identity_verified`: it's `true` only when a signed link in one of your emails identified the visitor, which is part of the [Commerce](/docs/commerce/connect-your-shop/#identify-visitors) alpha, and those events do drive automations.

## What the script sends

Each event carries, besides your type and properties:

| Field | Content |
| --- | --- |
| `anonymous_id` | A random visitor ID (`anon_…`), kept in the browser's local storage. |
| `email`, `contact_id` | What you passed to `identify`, if anything. |
| `context.page` | The page `url`, `path`, `title` and `referrer`. |
| `context.campaign` | The page's UTM parameters (`utm_source`, `utm_medium`, `utm_campaign`, `utm_term`, `utm_content`), as `source`, `medium`, `name`, `term` and `content`. |
| `context.locale`, `context.timezone` | The browser's language and time zone. |

Emailit adds `identity_verified` to events that name a contact (see [Identify visitors](#identify-visitors)) and stores the browser's user agent. It doesn't store IP addresses, and the script sets no cookies. It keeps the visitor ID under `emailit:aid` and the identity under `emailit:identity` in local storage; if local storage is blocked, they last only for the page.

Events are collected for a moment and sent together, at most 20 at a time. When the visitor leaves the page, the rest are sent with `sendBeacon`.

If you need consent for analytics on your site, load the script only after the visitor agrees.

## Allowed sites

`allowed_origins` limits which sites can send events with a key. Each entry is one of:

| Entry | Matches |
| --- | --- |
| `https://shop.example.com` | Exactly that origin: scheme, host and port. |
| `shop.example.com` | That host on any scheme or port. |
| `*.example.com` | `example.com` and every subdomain. |

An empty list accepts any site. Emailit checks the `Origin` header that browsers send with every request, so other websites can't send events with your key. A script outside a browser can set any header, though, so treat browser events as untrusted input and send orders and payments [from your backend](/docs/events/send-events/).

## Invalid events

Unlike the API, the script's endpoint stores the valid events of a request even when others are invalid. Invalid events are dropped, for example a `w:` event with a property of the wrong type, or an event of a new type once the workspace uses its plan's number of [event types](/docs/events/#limits). Use [debug](#debug) to see why.

## Debug

Turn on logging in the browser console in any of these ways:

- Call `emailit("debug", true)`.
- Add `data-debug` to the script tag.
- Open the page with `?emailit_debug=1` in the URL.

The console then shows each event and Emailit's answer, including the reasons for rejected events.

## Troubleshooting

| Answer in the console | Cause | Fix |
| --- | --- | --- |
| `403` `Origin not allowed for this tracking key` | The page's site isn't in **Allowed origins**. | Add it, for example both `https://example.com` and `https://www.example.com`, or use `*.example.com`. |
| `403` `This tracking key is disabled` | The key is turned off. | Turn it on in the **Tracking** tab. |
| `403` `Website tracking is included with Pro and Business.` | The workspace is on Pay as you go. | Upgrade to Pro or Business. |
| `404` `Unknown tracking key` | The key in the script URL was deleted or mistyped. | Copy the snippet again from the **Tracking** tab. |
| `422` with every event in `rejected` | None of the events was valid. | Check the reasons in `rejected`. |
| `429` | The key sent too many events in a short time. | Send fewer events per visitor, for example no event per scroll or mouse move. |
| Nothing at all | An ad or tracking blocker stopped the script, or it isn't on the page. | Test in a browser without blockers and check that the snippet is there. |

## Related

  - [Event tracking](/docs/events/): Scopes, names and where events go.
  - [Workspace events](/docs/events/workspace-events/): Define events and their properties.
  - [Tracking keys API](/docs/api-reference/tracking-keys/): Create, update and delete keys.
  - [Send events from your backend](/docs/events/send-events/): For orders and payments.

---
Source: https://emailit.com/docs/events/tracking-script/
