# Authentication

> Client-credentials tokens for the API, idempotency keys for safe retries, and single sign-on for the console.

## API: OAuth2 client credentials

Every v2 endpoint requires a **client-credentials token**. There are no
user passwords on the API: the token identifies your OAuth application,
and every message, quota and callback is attributed to that client.

```bash
curl -X POST https://nofy.encipher.co.tz/v1/oauth2/token/ \
  -d grant_type=client_credentials \
  -d client_id=YOUR_CLIENT_ID \
  -d client_secret=YOUR_CLIENT_SECRET
```

Use the token as a Bearer token until it expires (`expires_in` seconds),
then mint a fresh one. A token from any other grant type, or none at all,
is rejected:

```json
{
  "detail": "This endpoint requires a client-credentials token."
}
```

Your operator issues the client id and secret. The secret is shown once,
so store it like a password.

!!! warning "Never ship the secret in client apps"
    The client secret belongs on your servers only. A mobile app or browser
    bundle cannot keep a secret: route sends through your backend, which
    holds the secret and mints tokens.

## Idempotency

`POST /v2/messages` also requires an `Idempotency-Key` header. Replaying a
key returns the *original* response (status `202`) instead of sending
twice, so network retries and redelivered jobs on your side are safe.
Keys are scoped per client: two clients may use the same key without
colliding.

!!! tip "Derive the key from your own event"
    Use something stable such as `shift-8841-closed` rather than a random
    value generated at send time, so a retry after a crash reuses it.

## Console: staff sign-in

The operations console (`/ops/`) uses organization single sign-on. Console
pages also require superuser rights: signed-in staff without them see an
access notice, never the dashboard.
