# Tracking SDK

> Reference for customercatalyst.js — the script tag, the JavaScript API, batching, limits and errors.

`customercatalyst.js` sends product usage events from the browser to CustomerCatalyst. It has no dependencies and no build step. For the setup walkthrough, see [Track product usage](/integrations/tracking-sdk/).

## Install

### Script tag

```html
<script src="https://cdn.customercatalyst.io/customercatalyst.js" api-key="pk_…"></script>
```

| Attribute | Value |
|---|---|
| `api-key` | Your public key (`pk_…`) with the **Usage tracking** ability. Not `data-api-key`. |
| `debug` | Optional, no value. Turns on console logging (same as `CustomerCatalyst.debug = true`). |

The tag creates a ready instance at `window.customercatalyst`. Loading the script twice reuses the first instance. If something else already sits at `window.customercatalyst`, it is replaced and a console warning says so.

### Manual initialization

Leave out `api-key` and construct the instance yourself when you need more than one key on a page, the key is only known at runtime, or you bundle the file into your own build:

```html
<script src="https://cdn.customercatalyst.io/customercatalyst.js"></script>
<script>
  var cc = new CustomerCatalyst('pk_…');
  cc.identify({ customerId: 'CUSTOMER_ID' });
  cc.track('login');
</script>
```

Don't name your variable `CustomerCatalyst` or `customercatalyst`; those names belong to the class and the auto-created instance.

### Keys and sites

The key only works on the sites listed on it, matched against the browser's `Origin`. See [API keys](/settings/api-keys/). A page on an unlisted site gets `403` and the SDK stops sending on that key.

## API

| Member | Does |
|---|---|
| `new CustomerCatalyst(apiKey)` | Creates an instance. Throws if `apiKey` isn't a non-empty string. |
| `identify({ customerId, customerName? })` | Sets the customer for the following events. Required before `track()`. `customerId` is the customer's CustomerCatalyst ID, up to 255 characters. Throws if missing or too long. |
| `track(eventType, value?, metadata?)` | Queues one event. `eventType` is the metric slug, up to 255 characters. `value` defaults to `1`; it must be a finite number, 0 or more, and is rounded to 6 decimals. `metadata` must be a plain JSON-serialisable object, otherwise it is dropped with a warning and the event is still sent. Throws if `identify()` wasn't called or `eventType` is blank or too long. |
| `flush()` | Returns a promise. Sends everything queued for this key now and resolves when the queue is empty, or after 5 seconds at most. |
| `restart()` | Resumes a key that a fatal error stopped. Only useful if the cause was temporary; for a wrong key, create a new instance. |
| `stats` | `{ queued, droppedEvents, droppedBatches }` for this key. |
| `isStopped` | `true` once a fatal error stopped this key. `track()` is then ignored. |
| `CustomerCatalyst.debug` | Page-wide switch for console logging. Keys are logged as their last 4 characters only. |
| `CustomerCatalyst.init()` | What the script tag runs to create `window.customercatalyst`. You don't need to call it. |

```js
customercatalyst.identify({ customerId: 'CUSTOMER_ID', customerName: 'Acme' });
customercatalyst.track('report_generated', 1, { type: 'monthly' });

logoutButton.addEventListener('click', function (e) {
  e.preventDefault();
  customercatalyst.track('logout');
  customercatalyst.flush().then(function () { location.href = '/logout'; });
});
```

## Batching and delivery

- Events are batched: up to 50 events or about 28 KB per request, and at most 10 requests per second for the whole page.
- A burst of `track()` calls in the same moment waits 50 ms and goes out as one request.
- Each event gets a unique ID, so an event sent twice is stored once.
- When the page is hidden or closed, anything still queued is sent with `navigator.sendBeacon`.
- Several keys on one page queue separately: one key waiting or failing doesn't hold up another.

## Limits

| Limit | Value | When exceeded |
|---|---|---|
| Events per organization | 50,000 per hour | Refused until the next hour. The SDK waits and retries. |
| `customerId`, `eventType` | 255 characters each | `identify()` / `track()` throws. |
| `value` | 0 or more, up to 12 digits and 6 decimals | Stored as `1`. |
| Single event, with metadata | about 28 KB | Dropped with a warning. |
| Queue | 1,000 events per page, 500 per key | New events are dropped with a warning until it drains. |

Length is counted in characters (Unicode code points), so an emoji counts as one.

## Errors

| Response | SDK does |
|---|---|
| `401`, `403`, `404` | **Stops the key.** Discards its queue and ignores further `track()` calls. `401`: key wrong, revoked or without Usage tracking. `403`: site not on the key's list. |
| `400`, `413`, `422` | Drops that batch, keeps sending the rest. Counted in `stats`. |
| `408`, `429`, `5xx`, network errors | Retries with increasing waits, up to 30 seconds apart. On `429` it waits as long as the server asks, at most 5 minutes. A batch is dropped after 10 failed attempts. |

## Content Security Policy

If your product sends a `Content-Security-Policy`, allow the script and its endpoint. See [Content Security Policy](/developers/content-security-policy/#tracking-sdk).

## Browser support

Current versions of Chrome, Firefox, Safari, Edge and Opera. Internet Explorer is not supported.
