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

# Paginação, filtros e erros

> O envelope de lista, os parâmetros page e pageSize, os filtros por recurso e o formato de erro que toda a API do vendedor compartilha.

Aprenda estas três formas uma vez e você já sabe ler qualquer uma das 142 operações da API do vendedor. Elas não mudam de recurso para recurso.

## O envelope de resposta

Toda resposta bem-sucedida tem `success: true` e os dados em `data`. O que muda é se `data` é um objeto ou uma lista.

<CodeGroup>
  ```json Item theme={null}
  {
    "success": true,
    "data": {
      "id": "cmty05wl0000apyg1bx1566ej",
      "name": "Curso de Tráfego Pago 2026",
      "price": 49900
    }
  }
  ```

  ```json Lista theme={null}
  {
    "success": true,
    "data": [
      { "id": "cmty05wl0000apyg1bx1566ej", "name": "Curso de Tráfego Pago 2026", "price": 49900 }
    ],
    "pagination": { "page": 1, "pageSize": 2, "total": 6, "totalPages": 3 }
  }
  ```
</CodeGroup>

Nunca leia a raiz da resposta como se fosse o recurso. `data` é sempre o lugar certo, mesmo quando a lista tem um item só.

## Paginação

Toda rota cujo nome começa com "Listar" aceita dois parâmetros de query:

| Parâmetro  | Tipo    | Padrão | Limite      |
| ---------- | ------- | ------ | ----------- |
| `page`     | inteiro | `1`    | começa em 1 |
| `pageSize` | inteiro | `10`   | máximo 100  |

```bash theme={null}
curl -s "https://api.ephra.io/v1/products?page=1&pageSize=2" \
  -H "Authorization: Bearer $TOKEN"
```

O bloco `pagination` diz onde você está:

```json theme={null}
{ "page": 1, "pageSize": 2, "total": 6, "totalPages": 3 }
```

* `total` é a contagem de itens que passam pelos filtros, não o tamanho da página.
* `totalPages` é `ceil(total / pageSize)`.
* Você chegou ao fim quando `page === totalPages`, ou quando `data` vem vazio.

<Tip>
  Para varrer uma coleção inteira, use `pageSize=100` e pare quando `page` alcançar `totalPages`. Pedir `pageSize=500` devolve `422` com `"pageSize: pageSize deve ser no máximo 100"` — o teto é validado no schema.
</Tip>

```bash theme={null}
page=1
while :; do
  resp=$(curl -s "https://api.ephra.io/v1/products?page=$page&pageSize=100" \
    -H "Authorization: Bearer $TOKEN")
  echo "$resp" | jq -c '.data[]'
  total_pages=$(echo "$resp" | jq -r '.pagination.totalPages')
  [ "$page" -ge "$total_pages" ] && break
  page=$((page + 1))
done
```

<Warning>
  As rotas transacionais legadas **não** usam este envelope, e nem entre si concordam:

  * `GET /v1/transactions` responde `"totalRows": 2`, sem bloco `pagination`.
  * `GET /v1/subscriptions` responde `"pagination": { "page": 1, "limit": 20, "total": 0, "totalPages": 0 }` — repare em `limit`, não `pageSize` — mais um bloco `stats` com métricas agregadas.
  * `GET /v1/webhook` responde `"pagination": { "offset": 0, "pageSize": 20, "totalCount": 3, "currentPage": 1, "totalPages": 1, "hasNext": false, "hasPrevious": false }`.

  São rotas anteriores a esta convenção e o contrato delas está congelado: terceiros já integrados quebrariam. Veja a [API transacional](/api-reference/introducao).
</Warning>

## Filtros

Além de `page` e `pageSize`, cada recurso aceita os filtros que fazem sentido para ele. Todos são opcionais e combináveis — o efeito é `E`, não `OU`.

| Recurso                      | Filtros                                                     |
| ---------------------------- | ----------------------------------------------------------- |
| `GET /v1/products`           | `status`, `name`                                            |
| `GET /v1/coupons`            | `active`, `code`                                            |
| `GET /v1/offers`             | `productId`, `search`                                       |
| `GET /v1/order-bumps`        | `productId`, `offerId`                                      |
| `GET /v1/customers`          | `search`                                                    |
| `GET /v1/refund-requests`    | `status`, `refundChannel`, `startDate`, `endDate`, `search` |
| `GET /v1/balance-operations` | `from`, `to`, `type`, `direction`                           |
| `GET /v1/affiliates`         | `status`                                                    |
| `GET /v1/reports/*`          | `from`, `to`                                                |

Datas vão em ISO 8601 com fuso, e o intervalo é fechado nas duas pontas:

```bash theme={null}
curl -s "https://api.ephra.io/v1/balance-operations?from=2026-08-01T00:00:00.000Z&to=2026-08-31T23:59:59.000Z&direction=credit" \
  -H "Authorization: Bearer $TOKEN"
```

Cada página de referência lista os filtros da sua rota com o enum completo de valores aceitos. Um valor fora do enum não é ignorado: responde `422`.

## Erros

Um erro tem sempre a mesma forma — `success: false` e uma `message` em português, pronta para log:

```json theme={null}
{ "success": false, "message": "name: Required" }
```

O status HTTP é que carrega o significado.

| Status | O que aconteceu                                           | O que fazer                                               |
| ------ | --------------------------------------------------------- | --------------------------------------------------------- |
| `400`  | A requisição é válida, mas uma regra de negócio recusou   | Leia a `message`: ela nomeia a regra                      |
| `401`  | Token ausente, expirado ou de sessão encerrada            | Refaça `POST /auth` e repita a chamada                    |
| `403`  | A empresa está bloqueada para esta operação               | Fale com o suporte                                        |
| `404`  | O recurso não existe, foi excluído, ou é de outra empresa | Confira o id                                              |
| `409`  | O recurso não aceita a operação no estado em que está     | Releia o recurso antes de insistir                        |
| `422`  | O corpo ou a query não passaram na validação de schema    | Corrija o campo citado na `message`                       |
| `429`  | Requisições demais em pouco tempo                         | Espere e repita com recuo exponencial                     |
| `500`  | Falha interna                                             | Repita; se persistir, abra chamado com o horário e a rota |

### 422 nomeia o campo

A validação é por schema, então a `message` diz exatamente qual campo reprovou e por quê:

```bash theme={null}
curl -s -X POST https://api.ephra.io/v1/products \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" -d '{}'
```

```json theme={null}
{ "success": false, "message": "name: Required" }
```

```json theme={null}
{ "success": false, "message": "address.streetNumber: Required" }
```

O caminho usa ponto para campos aninhados. Corrigir é mecânico: o campo citado está faltando, com o tipo errado, ou fora do enum. Um enum errado devolve a lista inteira de valores aceitos:

```json theme={null}
{
  "success": false,
  "message": "status: Invalid enum value. Expected 'draft' | 'approved' | 'pending' | 'refused' | 'banned', received 'inexistente'"
}
```

### 400 é regra de negócio, não schema

O corpo passou pela validação, mas a operação não faz sentido para o estado atual do recurso:

```json theme={null}
{
  "success": false,
  "message": "Apenas transações com status 'paid' podem ser reembolsadas. Status atual: pending"
}
```

```json theme={null}
{
  "success": false,
  "message": "Valor solicitado (R$ 500.00) excede o limite disponível para antecipação (R$ 0.00). Você pode antecipar até 60% do volume liberado."
}
```

Não repita a chamada: o resultado será o mesmo até o estado mudar.

### 404 também significa "de outra empresa"

A API não distingue "não existe" de "existe mas é de outra empresa" — as duas situações respondem `404`. É de propósito: a diferença contaria a um estranho que aquele id existe.

```bash theme={null}
curl -s https://api.ephra.io/v1/products/cmtxx1udu0006hlg1brjc3jfs \
  -H "Authorization: Bearer $TOKEN"
```

```json theme={null}
{ "success": false, "message": "Produto não encontrado" }
```

<Note>
  As rotas transacionais legadas respondem `400` (não `422`) quando o corpo não bate com o schema, e acrescentam um array `details` com os erros campo a campo. É a única diferença de contrato de erro entre as duas superfícies.
</Note>

## Um cliente que aguenta o dia a dia

```bash theme={null}
chamar() {
  local resp status
  resp=$(curl -s -w '\n%{http_code}' "$@" -H "Authorization: Bearer $TOKEN")
  status=$(tail -n1 <<< "$resp")

  if [ "$status" = "401" ]; then
    TOKEN=$(curl -s -X POST https://api.ephra.io/auth -u "$EPHRA_KEY:$EPHRA_SECRET" | jq -r .token)
    resp=$(curl -s -w '\n%{http_code}' "$@" -H "Authorization: Bearer $TOKEN")
    status=$(tail -n1 <<< "$resp")
  fi

  head -n-1 <<< "$resp"
  [ "${status:0:1}" = "2" ]
}
```

Três hábitos cobrem quase tudo: reautenticar em `401`, ler `message` em `4xx` e repetir com recuo apenas em `429` e `5xx`.
