# Authentication

Every `/v1/*` endpoint needs a TikData key. Keys look like `thr_live_` followed by 48 hex characters, and each one belongs to exactly one project — that is how spend gets attributed without any client-side tracking code.

## Sending the key

Bearer token, on every request:

```bash
curl "https://tikdata.io/v1/whoami" \
  -H "Authorization: Bearer thr_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
```

`x-api-key` is accepted as an alternative header for clients that cannot set `Authorization`:

```bash
curl "https://tikdata.io/v1/whoami" \
  -H "x-api-key: thr_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
```

## What a key can do

| | consumer key | admin token |
|---|---|---|
| `/v1/post`, `/v1/posts`, `/v1/videos/bulk_stats` | yes | — |
| `/v1/profile`, `/v1/creator/stats` | yes | — |
| `/v1/raw/*` | yes, within scope | — |
| `/v1/whoami` | yes | — |
| `/v1/usage` | own usage only | every key |
| `/v1/account` | — | yes |
| `/v1/keys` | — | yes |

The admin token is sent as `x-admin-token`. It is the deployment's `INGEST_TOKEN` and is never issued to consumers.

## Scopes

A key can be restricted at creation to a set of upstream path prefixes:

```json
{ "label": "clip bot", "project": "clip-bot", "scopes": ["/api/v1/tiktok/", "/api/v1/instagram/"] }
```

Scopes apply to `/v1/raw/*`. A scoped key that reaches outside its prefixes gets **403** with the allowed prefixes in the `hint`. Keys with no scopes can reach any upstream path.

## Failure responses

| status | body | meaning |
|---|---|---|
| 401 | `{"error":"missing api key","hint":"send: Authorization: Bearer thr_live_..."}` | no key in either header |
| 401 | `{"error":"invalid or disabled api key"}` | key unknown, deleted, or `enabled: false` |
| 403 | `{"error":"key is not scoped for this path","hint":"allowed: ..."}` | scoped key, path outside scope |
| 401 | `{"error":"admin token required","hint":"send: x-admin-token: <INGEST_TOKEN>"}` | admin endpoint, no or wrong token |

## Key hygiene

- The plaintext is shown **once**, in the `POST /v1/keys` response. Only its SHA-256 hash is stored, so a lost key is replaced, not recovered.
- Disable before deleting when you want to keep the key's usage history attributable: `PATCH /v1/keys` with `enabled: false` stops it immediately; `DELETE` removes the record. Per-key usage rows survive deletion because `project` is denormalized onto them.
- Every response from an authenticated endpoint carries `x-router-project: <project>` so a caller can confirm which project it is being billed to.
