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

# Website tracking

> Record events and goals from a browser with a tracking token, using track.js or a direct call to /public/events.

`POST /public/events` takes the same payload and follows the same rules as [`POST /v1/events`](/api-reference/events). The difference is that it also accepts a **tracking token**, so a web page can call it. Unlike `/v1/events`, `email` and `contactId` are optional here. An event without either is stored anonymously.

| Credential        | Header                        | Notes                                                                                            |
| ----------------- | ----------------------------- | ------------------------------------------------------------------------------------------------ |
| Tracking token    | `Authorization: Bearer ict_…` | Created under **Settings → Tracking Tokens**. It can only record events, so it is safe to embed. |
| Workspace API key | `Authorization: Bearer ic_…`  | For server-side callers. Needs `events:write`.                                                   |

* CORS is open (`Access-Control-Allow-Origin: *`).
* If a token has allowed domains, a browser request from any other `Origin` answers `403`. Subdomains of an allowed domain pass. A request with no `Origin` header, such as one from a server, is accepted.
* The limit is 100 requests/minute per workspace, and bodies are capped at 64 KB.
* The response is `{ id, status }`, where `status` is `"recorded"` (`201`) or `"duplicate"` (`200`).

## With track.js

```html theme={null}
<script src="https://instantcampaign.ai/track.js" data-site-id="ict_YOUR_TOKEN"></script>
<script>
  ICTrack.identify("jane@example.com");
  ICTrack.goal("purchase", { value: 42.5, currency: "GBP", externalId: "order-10422" });
</script>
```

After `identify()`, `track.js` adds `email` to every event and goal it sends from that page. A goal can name its own contact with `{ email }`, and `ICTrack.reset()` stops sending the email. Nothing is stored between pages, so call `identify()` on each page load.

## Direct call

```bash theme={null}
curl -X POST https://instantcampaign.ai/api/public/events \
  -H "Authorization: Bearer ict_YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "event": "signup.completed", "email": "jane@example.com", "goalType": "signup" }'
```

<Note>
  `track.js` also sends anonymous pageview and time-on-page beacons for web analytics. The endpoint behind those beacons is part of the script's own protocol and is not a public API. Use `track.js` for page analytics, and use this endpoint for events.
</Note>
