## Headers base

| Header | Obrigatório | Descrição |
| --- | --- | --- |
| `Authorization: Bearer <beacon_session_token>` | Sim nas rotas autenticadas | Token da sessão Beacon resolvido para um utilizador local |
| `Content-Type: application/json` | Sim (POST/PATCH) | Formato do payload |
| `Stripe-Signature` | Sim nas rotas `/api/webhooks/stripe*` | Assinatura Stripe verificada contra o segredo do ambiente |

## Autenticação real

- O Bearer token é validado contra a sessão Supabase/Beacon e resolvido para um utilizador local.
- As rotas documentadas nesta secção exigem contexto de organização (`org`) e, na prática, papel `OWNER` ou `ADMIN`.
- `mode=test|live` é validado contra o hostname do pedido; `test.beacon.pt` não aceita `live` e `app.beacon.pt` não aceita `test`.

## Deduplicação real

- Não existe um header público genérico `Idempotency-Key` nas rotas Beacon actuais.
- `POST /api/billing/subscription/checkout` cria a chave de idempotência para a Stripe no servidor e reutiliza checkout pendente quando o pedido é logicamente o mesmo.
- `POST /api/webhooks/stripe` deduplica por `event.id`; duplicados devolvem `duplicate: true`.
- Rotas que enfileiram trabalho, como replay de webhook e reconciliação, devolvem `queueJobId` e podem reutilizar trabalho já em curso.

## Erros típicos

| Código | Significado | Ação |
| --- | --- | --- |
| `401` | token inválido ou ausente | renovar credencial e repetir |
| `403` | utilizador sem papel elegível | rever permissões da organização |
| `409` | já existe estado pendente ou bloqueio de domínio | consultar o estado actual antes de forçar nova operação |
| `422` | dados funcionais rejeitados pelo domínio | corrigir payload e repetir |

## Exemplo de pedido seguro

```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"}'
```
