# Authentication

Every request needs **two headers**:

```shell
curl https://hub.zenith-books.com/api/public/v1/me \
  -H "Authorization: Bearer zth_live_a1b2c3…" \
  -H "X-Company-Key: a0b6156c-2f3d-4e5a-9b8c-1d2e3f405191"
```

## Creating a token

1. In the Zenith app, go to **Settings → API tokens**.
2. Create a token. The plaintext value (`zth_live_…`) is shown **exactly
   once** — copy it now. Only a SHA-256 hash is stored; nobody, including
   Zenith staff, can recover it later.
3. Copy the **company key** shown alongside it — that's your `X-Company-Key`
   value.

One token is permanently bound to **one company**. If you work with multiple
companies, create one token per company. The explicit `X-Company-Key` header
exists as a guard: if the header and the token's bound company ever disagree,
the request fails with `400 invalid_company_key` instead of silently reading
the wrong books.

## Token lifecycle

- Tokens are **read-only** — even a leaked token cannot modify or delete
  anything.
- Optional expiry date at creation; expired tokens fail with
  `401 token_expired`.
- Any member of the company can revoke any of its tokens from
  **Settings → API tokens** (`401 token_revoked` afterwards).
- `GET /me` shows the token's `last_used_at` — useful to confirm an
  integration is alive before revoking old tokens.

## Keep it secret

The token is a bearer credential — treat it like a password:

- Store it in environment variables or a secret manager, never in source
  control.
- **Server-to-server only.** The API sends no CORS headers, so browsers block
  cross-origin calls by design — never embed the token in frontend code.
- Rotate by creating a new token first, switching your integration, then
  revoking the old one.
