# Authentication

The Public API supports two authentication methods. Pick based on the caller.

## API key

Send your key in the `x-api-key` header on every request:

```bash
curl https://api.ledgerbee.com/api/v1/company \
  -H "x-api-key: YOUR_API_KEY"
```

Best for server-to-server integrations you control. Keys are scoped and can be
revoked from the LedgerBee app.

- **Never send an API key as a Bearer token.** `Authorization: Bearer <key>`
  is rejected with `401` — that header carries OAuth access tokens only.
- The key can also be passed as an `?x-api-key=` query parameter; prefer the
  header — query strings leak into access logs.
- A key can carry an **IP allowlist**. Requests from other IPs get `401` with
  `details.reason: "ip_whitelist_mismatch"`.
- Inspect a key's own scopes, allowlist, and expiry with `GET /v1/api-key`,
  authenticated with the key itself.

## OAuth2 client credentials

For machine-to-machine clients that prefer short-lived bearer tokens. Request a
token from the token endpoint, then send it as a `Bearer` token.

```bash
# 1. Exchange client credentials for an access token
curl -X POST https://api.ledgerbee.com/api/v1/auth/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials" \
  -d "client_id=YOUR_CLIENT_ID" \
  -d "client_secret=YOUR_CLIENT_SECRET"

# 2. Call the API with the returned access_token
curl https://api.ledgerbee.com/api/v1/company \
  -H "Authorization: Bearer ACCESS_TOKEN"
```

Scopes are configured **per client** under **Marketplace → OAuth Clients** —
scopes requested in the token call are ignored in favour of the client's
configured set.

- The `client_secret` is shown **once** at creation; store it securely.
- Tokens expire after **3600 seconds** — re-request when one expires.
- Revoking the client, or changing its scopes or IP allowlist, invalidates
  outstanding tokens; mint a fresh one.
- The partner provisioning scopes (`portal-provision`, `portal-sso-mint`) are
  **API-key only** — they can never be granted to an OAuth or MCP client. See
  **[Portal SSO](/guides/portal-sso)**.

## Common errors

| Symptom | Cause | Fix |
|---|---|---|
| `401` with `Authorization: Bearer <api-key>` | A raw API key sent as a Bearer token | Use the `x-api-key` header; `Bearer` is for OAuth tokens |
| `401 invalid_token` with a real OAuth token | Token expired, or its client/grant was revoked | Mint a fresh token |
| `401` with `reason: ip_whitelist_mismatch` | Request IP not on the key's allowlist | Add the IP, or clear the allowlist |
| `403 INSUFFICIENT_PERMISSIONS` | Key/token lacks the required scope | Add the scope to the key or client — see **[Errors](/guides/errors)** |

## Dynamic Client Registration (DCR)

For agent/MCP clients that self-register, the API exposes
[RFC 7591](https://datatracker.ietf.org/doc/html/rfc7591) Dynamic Client
Registration:

```
POST https://api.ledgerbee.com/api/v1/oauth/register
```

## Discovery (`.well-known`)

The authorization server and protected-resource metadata are published for
automatic client configuration:

- `/.well-known/oauth-authorization-server`
- `/.well-known/oauth-protected-resource`
- `/.well-known/openid-configuration`
- `/.well-known/jwks.json`

These power the OAuth 2.1 / MCP authorization flows described in the
**[MCP guide](/guides/mcp)**.
