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

# Authentication

> How tenant API keys work: format, scopes, errors, and rate limits.

Every endpoint on this API authenticates with a tenant API key. There's no OAuth flow and no session cookie — one key, sent on every request.

## Key format

A key looks like:

```
cmp_live_5f2a9c1e8b3d4a6f0e7c2b1d9a8f3e5c6d7b8a91
```

The `cmp_live_` prefix is followed by 40 hexadecimal characters. Send it as a bearer credential on the `Authorization` header:

```
Authorization: Bearer cmp_live_5f2a9c1e8b3d4a6f0e7c2b1d9a8f3e5c6d7b8a91
```

## Org scope

A key belongs to one organization. Every request made with it acts on that organization only — there's no way to pass a different organization ID in the request and have it honored. If you present a key on a campaign code or call ID that belongs to another organization, you get a `404`, not a `403`, so campaign codes and call IDs can't be probed across tenants.

## Scopes

A key carries one or more scopes, and each endpoint requires a specific one:

| Endpoint                                            | Required scope |
| --------------------------------------------------- | -------------- |
| `POST /dni/session`                                 | `enroll`       |
| `POST /dni/campaigns/{campaign_code}/buyer-report`  | `execute`      |
| `POST /tracking/campaigns/{campaign_code}/outbound` | `execute`      |

A key without the scope an endpoint requires gets a `403` with `insufficient_scope`. If your integration needs to call both DNI session creation and the reporting or outbound endpoints, ask support for a key with both `enroll` and `execute` scopes.

A `403` with `tracking_disabled` means something different: API access isn't switched on for your organization yet. It's enabled per workspace, alongside your key. Contact support and ask for API access to be enabled.

## Errors

Any problem with the credential itself — missing, malformed, unknown, revoked, or expired — returns the same response, on purpose:

```json theme={null}
{
  "detail": "invalid_api_key"
}
```

with status `401`. The response never says which of those it was; that's deliberate, so a caller probing for valid keys can't learn anything from the error.

## Rate limits

`POST /dni/session` is rate-limited per organization on a fixed one-minute window: 300 requests per minute by default. Going over the limit returns `429` with `{"detail": "rate_limited"}`. The other two endpoints don't carry this limiter today.

## Handling your key

Treat a key like any other production secret:

* Call the API from your own backend, not from client-side JavaScript. Anything shipped to a browser is visible to whoever opens the network tab.
* Store it in your server's secret configuration, not in source control or a client-side bundle.
* If a key is ever exposed — pasted into a support ticket, committed to a repo, shown in a screenshot — treat it as compromised and ask support to rotate it.

## Getting a key

There's no self-serve key management in the dashboard today. Compensable support issues API keys for your organization, and can add scopes, rotate a key, or revoke one on request. Contact support with the scopes your integration needs (`enroll`, `execute`, or both) to get started.
