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

# API Keys

> Autenticação e escopos da API affiliate Lomadee

## Visão geral

A API affiliate usa chaves no header `x-api-key`. Cada chave de afiliado possui **escopos** que limitam quais endpoints podem ser chamados.

## Formato da chave

* **Legado (v1):** string de 32 caracteres — continua válida durante a transição
* **Novo (v2):** `lmd_{env}_{prefix}_{secret}` — lookup por prefix + hash; o segredo não é armazenado em texto plano

## Escopos affiliate

| Escopo            | Endpoints                                                             |
| ----------------- | --------------------------------------------------------------------- |
| `brands:read`     | `GET /affiliate/brands`, `GET /affiliate/brands/{brandId}`            |
| `campaigns:read`  | `GET /affiliate/campaigns`, `GET /affiliate/campaigns/{campaignId}`   |
| `channels:read`   | `GET /affiliate/channels`, `GET /affiliate/channels/{channelId}`      |
| `orders:read`     | `GET /affiliate/orders`, `GET /affiliate/orders/{orderId}`            |
| `products:read`   | `GET /affiliate/products`                                             |
| `shortener:read`  | `GET /affiliate/shortener/urls`, `GET /affiliate/shortener/urls/{id}` |
| `shortener:write` | `POST /affiliate/shortener/url`                                       |

Todos os escopos declarados em uma rota são obrigatórios (AND). Sem o escopo necessário, a API retorna `403 Forbidden`.

## Partner scopes

Keys of type `partner` authenticate `/api/partner/*` (catalog, no affiliate context).

| Scope                | Endpoints                                                               |
| -------------------- | ----------------------------------------------------------------------- |
| `organizations:read` | `GET /api/partner/organizations`                                        |
| `brands:read`        | `GET /api/partner/brands`, `GET /api/partner/brands/{brandId}`          |
| `campaigns:read`     | `GET /api/partner/campaigns`, `GET /api/partner/campaigns/{campaignId}` |

New partner keys include all three scopes. Older keys with only `organizations:read` still work on that route; brands and campaigns require the matching scope.

An `affiliate` key on a partner route (and the reverse) returns `401`.

## Rate limiting

Limite global: **60 requests / 60 seconds** por API key e IP do cliente.

| Header                  | Descrição                         |
| ----------------------- | --------------------------------- |
| `X-RateLimit-Limit`     | Máximo de requests na janela      |
| `X-RateLimit-Remaining` | Requests restantes na janela      |
| `X-RateLimit-Reset`     | Unix timestamp do reset da janela |

Ao exceder o limite, a API retorna `429` com código `TOO_MANY_REQUESTS` e headers `Retry-After`, `X-RateLimit-Limit`, `X-RateLimit-Remaining` (0) e `X-RateLimit-Reset`.

Requisições com campos desconhecidos no body (ex.: `POST /affiliate/shortener/url`) retornam `400` por validação estrita (`whitelist`).

## Erros de autenticação

| Status | Código         | Causa                                            |
| ------ | -------------- | ------------------------------------------------ |
| `401`  | `UNAUTHORIZED` | Header ausente, chave inválida ou tipo incorreto |
| `403`  | `FORBIDDEN`    | Chave válida sem escopo necessário               |

```json theme={null}
{
  "success": false,
  "message": "Insufficient scope",
  "code": "FORBIDDEN"
}
```

## Provisionamento

Chaves affiliate são criadas via **GraphQL** no serviço open-api (autenticação de dashboard com JWT em `x-api-token`):

| Operação       | Descrição                               |
| -------------- | --------------------------------------- |
| `listApiKeys`  | Lista chaves do usuário                 |
| `createApiKey` | Cria chave com nome e escopos opcionais |
| `rotateApiKey` | Gera novo segredo mantendo metadados    |
| `revokeApiKey` | Revoga chave                            |

A resposta de criação/rotação inclui o segredo completo **apenas uma vez** no campo `apiKey`. Guarde-o com segurança.
