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

# Tracker reference

> Options, captured fields, excluded paths and token rotation for the @usenotra/sdk tracker.

| Option | Default | Meaning |
| - | - | - |
| `token` | required | Per organization ingest token |
| `endpoint` | `https://ingest.usenotra.com` | Ingest origin, and the SDK appends `/api/geo/ingest` |
| `exclude` | `["/api"]` | Paths to skip. Pass `[]` to disable |
| `sample` | `1` | Fraction of eligible requests to send, 0 to 1 |
| `onError` | none | Called with anything that goes wrong, since the SDK never throws |
| `fetch` | global `fetch` | Injectable fetch, for tests |
| `waitUntil` | none | React Router only. Keeps the send alive after the response on serverless hosts |

When you omit `endpoint`, the SDK posts directly to `https://ingest.usenotra.com` and does not retry through the dashboard. To use the dashboard ingestion route, set `endpoint: "https://app.usenotra.com"` and redeploy your site. Self-hosted installations must set `endpoint` to their own ingestion origin.

The tracker captures only `GET` requests that look like pages, and it skips anything under `/_next/`, `/_nuxt/`, `/_vercel/`, `/_astro/`, `/_app/immutable/` and `/static/`, plus any path ending in a common static extension, although it always captures `llms.txt` and `llms-full.txt`. An `exclude` entry can be a string prefix, a `RegExp` or a `(request, url) => boolean` function.

The envelope contains the timestamp, method, URL, client IP, edge location headers when present, referer, user agent, accept and accept-language headers and a request id. The SDK does not read or send the request body, cookies or any other headers, and the POST uses `keepalive` and a 2 second timeout.

## Browser script (notra.js)

The [script tag](https://www.usenotra.com/docs/analytics/install/script-tag.md) counts people rather than server requests. It takes these attributes:

| Attribute | Meaning |
| - | - |
| `data-key` | Required. The project's public key |
| `data-api` | Beacon endpoint. Defaults to `/api/collect` on the script's origin |
| `data-hash` | Count `#hash` changes as page views. Notra stores `#/route` fragments as pages but never other fragments |

Web vitals (LCP, FCP, TTFB, CLS and INP) and outbound link clicks are always recorded. `/api/notra.plus.js` is the same script with JavaScript error reporting added.

`/api/notra.js` and `/api/notra.plus.js` always serve the latest build. `/api/notra.<version>.js` and `/api/notra.plus.<version>.js` serve one build forever, cached for a year, with CORS enabled, so you can load it with `integrity` and `crossorigin="anonymous"`. The install panel and `webSnippet` hand out the pinned tag, which never updates on its own. See [Pinned builds and Subresource Integrity](https://www.usenotra.com/docs/analytics/install/script-tag.md#pinned-builds-and-subresource-integrity).

## Install snippets from the API

Open **Analytics** in the GEO sidebar and stay on the **Agent** tab. Until the first event arrives, the page shows **No activity yet** with the full install panel of **Install the package**, **Set your token** and **Add the proxy**, and the token appears in the **Set your token** section. The **Copy agent prompt** button copies an instruction block you can paste into a coding agent.

From the API, reading the snippets needs `traffic.read` and issuing the token needs `traffic.write`:

```bash theme={"system"}
curl https://api.usenotra.com/v1/geo/ingest/setup \
  -H "Authorization: Bearer $NOTRA_API_KEY"
```

```bash theme={"system"}
curl -X POST "https://api.usenotra.com/v1/geo/ingest/token?projectId=$PROJECT_ID" \
  -H "Authorization: Bearer $NOTRA_API_KEY"
```

Both return `ingestUrl`, `snippet` (the Next.js snippet), `snippets` with `next`, `nuxt`, `tanstack`, `astro`, `sveltekit`, `reactRouter` and `netlify` keys, `drainUrls` with the `vercel` and `cloudflare` log drain endpoints, and the notra.js install: `webKey`, `webSnippet` (the script tag, pinned to the current build with its `integrity`) and `webScript` and `webPlusScript`, each with the auto-updating `url`, the `pinnedUrl` of the current build and its `integrity` value. Every web key belongs to one project, so pass `projectId` for that project's key, or omit it for the organization's oldest project's key. The web key is public, so reading it only needs `traffic.read`. The token endpoint also returns `token`. The ingest routes are organization-level, so pass `projectId` to bind the token to one project or omit it to track the whole organization. Issuing a token does not invalidate tokens issued earlier, so install the same project token on every domain the project tracks, and add extra hostnames under **Tracked domains** in GEO settings.

## Rotating the token

Open **API Keys** and press **Rotate** on the tracking token card. Rotation covers the whole organization, so every project gets a new token.

```bash theme={"system"}
curl -X POST "https://api.usenotra.com/v1/geo/ingest/rotate-token?projectId=$PROJECT_ID" \
  -H "Authorization: Bearer $NOTRA_API_KEY"
```

Rotation invalidates every tracking token previously issued for the organization and returns a fresh one. Because Notra immediately rejects events from deployments that still send the old token, update `NOTRA_GEO_TOKEN` everywhere before you rotate.

The public notra.js key can only be rotated in the dashboard, by an owner or admin, because the API has no endpoint for it. Pages that still load the old key stop counting until they get the new snippet.


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