# Authentication

Authenticate every request with a supplier-scoped bearer token. One
authentication scheme covers everything — send an `Authorization` header:

```bash
curl "https://sandbox-pay.mmokick.com/api/v1/whoami" \
  -H "Authorization: Bearer $CK_TEST_TOKEN" \
  -H "Accept: application/json"
```

Expected `200` response:

```json
{
  "token": {
    "id": "01JZV9AB2CD4EF6GH8JK0MN2PQ",
    "name": "repricer-bot",
    "mode": "test",
    "scopes": ["offers:read", "offers:write", "pricing:read"],
    "created_at": "2026-06-02T09:14:00Z",
    "last_used_at": "2026-07-11T13:59:41Z"
  },
  "supplier": {
    "id": "01JZV8K2E4Q6S8A0C2E4G6J8KM",
    "name": "Keyforge Ltd",
    "status": "active"
  },
  "environment": "sandbox",
  "rate_limits": { "reads": 600, "writes": 120, "sync": 300, "bulk": 12, "simulate": 120, "test": 6 }
}
```

`GET /whoami` is your integration smoke test: it confirms the token is valid, tells
you which scopes it carries, and reports the environment it belongs to. Use it first
in any new integration.

## Get a token

Create tokens yourself in the supplier portal, under **Settings → API tokens → Create**
(route `supplier.developers.tokens`). Two-factor authentication is required to create a
token. A token is **shown once** at creation and hashed at rest; if you lose it, revoke it
and create another.

- Sandbox access is instant and auto-approved: submitting the supplier application in the
  portal approves your supplier into the sandbox on the spot, so you can create a
  `ck_test_` token in the portal (env toggle = Sandbox) — or use the published seeded
  token — and call the sandbox base URL within minutes. One portal manages both your Live
  and Sandbox tokens, so the same **Settings → API tokens** flow works for either.
  See [Environments](https://pay.mmokick.com/developers/getting-started/environments).
- You may hold up to **25 tokens per environment**. Tokens stay valid until you revoke
  them, and you can revoke any token instantly in the portal. `last_used_at` is tracked
  and displayed so you can spot and retire stale tokens.

Export your token once and reuse it across every example in these guides:

```bash
export CK_TEST_TOKEN="ck_test_..."
```

## The header

```
Authorization: Bearer <token>
```

Tokens carry a prefix that names the environment:

| Prefix | Environment | Base URL |
|---|---|---|
| `ck_test_` | Sandbox | `https://sandbox-pay.mmokick.com/api/v1` |
| `ck_live_` | Production | `https://pay.mmokick.com/api/v1` |

A token is the prefix plus 40 random base62 characters, for example
`ck_live_1u2P9zXqK7mA4dT8rW3yB6nC0eF5gH2jL9sV4xQ7`. Use tokens for
server-to-server calls: the API sends no CORS headers, so browser use is
unsupported by design. Keep tokens out of client-side code, URLs, and logs.

Match the token prefix to the host. Using a `ck_test_` token against production (or a
`ck_live_` token against sandbox) returns `401` with code `wrong_environment` — the
request always fails loudly rather than falling back silently.

## Scopes

Scopes are assigned at token creation; the default is all scopes. A token missing the
scope an endpoint requires gets `403` with code `insufficient_scope`.

| Scope | Grants |
|---|---|
| `catalog:read` | Products, categories |
| `pricing:read` | Fee schedule, pricing simulation |
| `offers:read` / `offers:write` | Offers read / create, update, activate, price, stock |
| `inventory:read` / `inventory:write` | Inventory list / upload, invalidate |
| `orders:read` | Orders read model |
| `balance:read` | Balance and ledger |
| `webhooks:manage` | Webhook endpoints, deliveries log, test-fire, resend |

Create a token with only the scopes an integration needs. A repricer bot, for example,
needs `offers:write` and `pricing:read` and nothing else.

## Failure responses

Every authentication and authorization failure is an RFC 9457 problem+json body with a
stable machine `code` you switch on. Codes stay stable; `title` and `detail` text may change.

**`401` — the token is the problem.** You are not (yet) authenticated:

```json
{
  "type": "https://pay.mmokick.com/developers/errors#invalid_token",
  "title": "Invalid token",
  "status": 401,
  "code": "invalid_token",
  "detail": "The bearer token is unknown.",
  "trace_id": "req_9f3b2c61a8d44e0f"
}
```

The `401` codes and what to do:

| `code` | Meaning | What to do |
|---|---|---|
| `unauthenticated` | The `Authorization` header is missing or malformed | Send `Authorization: Bearer <token>` |
| `invalid_token` | The token is unknown | Check the token; create a new one in the portal |
| `token_revoked` | The token was revoked in the portal | Issue a new token |
| `wrong_environment` | A `ck_test_` token was used on production, or `ck_live_` on sandbox | Use the host that matches the prefix |

**`403` — the token is valid, but not allowed here.** Either it lacks a scope or the
supplier account is suspended:

```json
{
  "type": "https://pay.mmokick.com/developers/errors#insufficient_scope",
  "title": "Insufficient scope",
  "status": 403,
  "code": "insufficient_scope",
  "detail": "This token is missing the required scope: offers:write.",
  "trace_id": "req_1a2b3c4d5e6f7081"
}
```

| `code` | Meaning | What to do |
|---|---|---|
| `insufficient_scope` | The token lacks a required scope | Issue a token that carries the scope |
| `supplier_suspended` | The supplier account is suspended by staff | Contact support; `GET /whoami` still works |

A request for another supplier's resource returns `404` with code `not_found` (rather
than `403`), keeping the existence of resources you cannot access private.

## Token hygiene

- **Rotate** by creating a new token and revoking the old one — create-then-revoke is
  the rotation for API tokens.
- **Revoke immediately** if a token is exposed. Revocation takes effect at once; the
  next call with a revoked token gets `401` / `token_revoked`.
- Tokens stay out of webhook payloads and every response body. If you see a
  token in a payload, it did not come from us.
- Quote the `X-Request-Id` header (echoed in error bodies as `trace_id`) in any support
  request — it lets us find the exact call in our logs.

## Next steps

- [Environments](https://pay.mmokick.com/developers/getting-started/environments) — base URLs, the sandbox, and the magic test values.
- [Your first API call in 5 minutes](https://pay.mmokick.com/developers/getting-started/first-call) — from token to a live response.
- [Errors](https://pay.mmokick.com/developers/errors) — the full machine error-code catalog.
