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

# Authentication

> Secret keys, OAuth2 client credentials, and customer identity tokens.

Every request carries a bearer token. Which kind depends on who you are.

## Secret keys (server-to-server)

The common case. Pass your secret key as a bearer token:

```bash theme={null}
curl https://api.useduro.com/v1/plans \
  -H "Authorization: Bearer sk_test_•••••••••••••••••"
```

* The **prefix selects the mode**: `sk_test_…` → sandbox, `sk_live_…` → live.
* Keys are **hashed at rest** — Duro can't show you a key again after creation, so store it securely.
* Keys carry **full scope** within their tenant. Treat a live secret key like a password.
* A publishable key (`pk_…`) is safe to expose in the browser/SDK; it can start checkout sessions but can't read or mutate data.

<Warning>
  Never ship a `sk_` key to a browser or mobile app. Use the publishable key and the [inline SDK](/payments/inline-sdk) client-side; keep secrets on your server.
</Warning>

## OAuth2 (client credentials)

For platforms acting on a tenant's behalf, exchange a client id/secret for a short-lived token:

```bash theme={null}
curl https://api.useduro.com/v1/oauth/token \
  -H "Content-Type: application/json" \
  -d '{ "grant_type": "client_credentials", "client_id": "cid_test_•••", "client_secret": "csec_test_•••" }'
```

Returns an access token valid for **60 minutes**. Use it exactly like a secret key (`Authorization: Bearer <token>`). The client id prefix carries the mode.

## Customer identity tokens

The customer-facing endpoints (`/identity/me`, `/portal/*`) use a token issued after a customer verifies their phone over WhatsApp:

```mermaid theme={null}
flowchart LR
    OTP["POST /identity/otp { phone }"] --> CODE["WhatsApp 6-digit code"]
    CODE --> VERIFY["POST /identity/verify { phone, code }"]
    VERIFY --> TOKEN["{ token } — 30-day identity session"]
    TOKEN --> USE["Authorization: Bearer <token><br/>on /identity & /portal"]
```

Identity tokens are scoped to a person, not a tenant — they resolve to a `CustomerIdentity` and are rejected by the merchant `/v1` API. See [Universal Identity](/identity/universal-identity).

## Errors

A missing or invalid credential returns `401`. An authenticated request that lacks the required permission returns `403`. See [Errors](/api-reference/errors).
