## Base headers

| Header | Required | Description |
| --- | --- | --- |
| `Authorization: Bearer <beacon_session_token>` | Yes on authenticated routes | Beacon session token resolved to a local user |
| `Content-Type: application/json` | Yes (POST/PATCH) | Payload format |
| `Stripe-Signature` | Yes on `/api/webhooks/stripe*` | Stripe signature validated against the environment secret |

## Real authentication

- The bearer token is validated against the Supabase/Beacon session and resolved to a local user.
- The routes documented here require organization context (`org`) and, in practice, `OWNER` or `ADMIN` membership.
- `mode=test|live` is validated against the request hostname. `test.beacon.pt` rejects `live` and `app.beacon.pt` rejects `test`.

## Real deduplication

- There is no generic public `Idempotency-Key` header on the current Beacon routes.
- `POST /api/billing/subscription/checkout` generates the Stripe idempotency key server-side and can reuse a pending checkout for the same logical request.
- `POST /api/webhooks/stripe` deduplicates by `event.id`; duplicates return `duplicate: true`.
- Replay and reconciliation-style routes return `queueJobId` and may reuse work that is already in progress.

## Common errors

| Code | Meaning | Action |
| --- | --- | --- |
| `401` | invalid or missing token | refresh credentials and retry |
| `403` | caller is not eligible | review organization permissions |
| `409` | pending state or domain conflict already exists | inspect current state before forcing a new action |
| `422` | domain validation failed | fix the business payload and retry |

## Safe request example

```bash
curl -X POST "https://test.beacon.pt/api/billing/subscription/checkout?org=acme&mode=test" \
  -H "Authorization: Bearer <beacon_session_token>" \
  -H "Content-Type: application/json" \
  -d '{"plan":"plus","billingCycle":"monthly"}'
```
