# Tracker API

> Reference for the ClickClacks browser tracker: every script attribute, every command (event, identify, reset, optOut, optIn), the call queue, browser storage keys, network requests and browser support.

- Canonical URL: https://clickclacks.io/docs/tracker-api
- Section: Reference
- Last updated: 2026-09-26

Everything the browser tracker understands: the attributes on its script tag and the five commands it takes. There’s no other surface.

## Script attributes {#attributes}

```html
<script async src="https://app.clickclacks.io/c.js" data-key="pk_live_3f9a1c7e5b2d4f6a8c0e1b3d" data-domains="acme.com,www.acme.com,app.acme.com"></script>
```

| Attribute |  | Meaning |
| --- | --- | --- |
| `src` | Required | `https://app.clickclacks.io/c.js`, or `https://<your custom domain>/c.js`. Events go to the same origin the script loads from. |
| `async` | Recommended | Loads without blocking the page. |
| `data-key` | Required | The source’s browser key, `pk_live_…`. Without it the tracker does nothing. |
| `data-domains` | Required | Comma-separated hostnames the tracker may run on, matched exactly and case-insensitively. Links between them carry identity. |
| `data-query` | Optional | `off` sends page paths and campaign data without the query string. |
| `data-capture` | Optional | `off` stops heatmap captures from this page. Events are unaffected. |
| `nonce` | Optional | Your CSP nonce. Passed on to the heatmap helper the tracker loads. |

The tracker reads these from its own tag when it starts, and only starts when the page’s hostname is in `data-domains`, `data-key` is set, and browser storage works. Otherwise `window.clickclacks` is never defined.

## Commands {#commands}

```js title="signature"
window.clickclacks(command: string, ...args): void
```

- Every command returns `undefined` and never throws. A call with the wrong arguments, or an unknown command, is ignored.
- While the visitor is opted out, every command except `optIn` and `optOut` is ignored.
- `reset`, `optOut` and `optIn` are also methods: `clickclacks.reset()`.

### event {#event}

```js
clickclacks('event', 'Report exported', { format: 'csv', rows: 1200 })
```

- **name**: a string of 1–128 characters, not starting with `$`.
- **properties** (optional): a plain object. Functions and `undefined` values are dropped. Over 2 KB of JSON, the event is sent without them.
- The page is added as `path`: the pathname only, never the query string or `#fragment`. Reports read it as **Page**. Pass your own `path` and yours is kept.
- After `identify`, the ID is added as `$distinct_id`.

Events sent from your servers (the [API](https://clickclacks.io/docs/api.md) and the SDKs) carry no page. Add `path` to their properties if you want one.

Guide: [Custom events](https://clickclacks.io/docs/events.md).

### identify {#identify}

```js
clickclacks('identify', 'user_4821', { plan: 'pro' })
```

- **id**: a string of 1–128 characters, your own user ID.
- **traits** (optional): a plain object, same rules as event properties. Over 2 KB, only the ID is sent.
- Sends a `$identify` event with `distinct_id` and the traits, and saves the ID for later events in this browser.
- Links this browser to anyone who identified with the same ID elsewhere. `$identify` events are free.

Guide: [Identify people](https://clickclacks.io/docs/identify.md).

### reset {#reset}

```js
clickclacks('reset')            // or clickclacks.reset()
```

Forgets the identified ID, drops events not sent yet, and starts a new anonymous ID and session. Tracking continues. Call it on sign-out.

### optOut {#opt-out}

```js
clickclacks('optOut')           // or clickclacks.optOut()
```

Drops events not sent yet, deletes the tracker’s IDs, stops all tracking, and remembers the choice (`cco`) so it holds on later page loads on this hostname.

### optIn {#opt-in}

```js
clickclacks('optIn')            // or clickclacks.optIn()
```

Forgets the opt-out and resumes tracking with new IDs. Counts the current page if the opt-out stopped it being counted. Guide: [Consent and opt-out](https://clickclacks.io/docs/consent.md).

## The call queue {#queue}

If `window.clickclacks.q` is an array when the tracker starts, each entry (an `arguments` list) is replayed in order, before the first pageview. That lets you call the tracker before it has loaded:

```html
<script>
  window.clickclacks = window.clickclacks || function () {
    (window.clickclacks.q = window.clickclacks.q || []).push(arguments)
  }
</script>
```

## Browser storage {#storage}

No cookies. Everything is in the page’s own origin, so each hostname has its own copy.

| Key | Where | Holds |
| --- | --- | --- |
| `ccp` | localStorage | The browser’s anonymous ID, `per_` + 32 hex characters. |
| `cci` | localStorage | The ID passed to `identify`. |
| `cco` | localStorage | `1` while the visitor is opted out. |
| `_cc_cap` | localStorage | Markers for heatmap captures already taken from this browser. |
| `ccs` | sessionStorage | The session ID, `ses_` + 32 hex characters. |
| `ccl` | sessionStorage | When the session was last active. After 30 minutes without an event, the next one starts a new session. |
| `ccc<key>` | sessionStorage | The source’s heatmap and click-capture settings, cached for the tab. |

## Network requests {#network}

| Request | What for |
| --- | --- |
| `GET /c.js` | The tracker, from `app.clickclacks.io` or your custom domain. |
| `POST /api/ingest?key=…` | Event batches, to `app.clickclacks.io`. On a custom domain: `POST /e?key=…`. Sent as `text/plain` JSON, without credentials. |
| `GET /api/heatmap-config?key=…` | The source’s capture and click settings, to `app.clickclacks.io`. Cached for the tab session, and asked again only on pages that may still need a capture. |
| `GET /heatmap-screenshot.js` | Only when this browser is asked to take a heatmap capture, from the same origin as `c.js`. |
| `POST /api/heatmap-capture?key=…` | A masked heatmap capture, to `app.clickclacks.io`. |

Events are batched: sent every 5 seconds, as soon as 25 are waiting, and when the page is hidden or closed. A failed batch is kept and retried with the next one, up to 100 waiting events.

## Browser support {#support}

The tracker is a small ES5 script with no dependencies. It needs `URL`, `localStorage` and `sessionStorage`, which every current browser has. It sends with `fetch` and `keepalive`, falling back to `sendBeacon`. Where storage is blocked it stays off rather than break your page.
