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

# Tratamento de erros

> Todos os erros possíveis e como tratá-los

## Formato do envelope de erro

Todos os erros seguem o mesmo formato:

```json theme={null}
{
  "error": {
    "type": "invalid_request_error",
    "code": "missing_subject",
    "message": "subject é obrigatório.",
    "param": "subject"
  }
}
```

| Campo     | Descrição                                        |
| --------- | ------------------------------------------------ |
| `type`    | Categoria do erro                                |
| `code`    | Código específico — use para lógica programática |
| `message` | Descrição legível em português                   |
| `param`   | Campo que causou o erro (quando aplicável)       |

## Tabela de erros

| HTTP | `type`                  | `code`                     | Quando ocorre                                                                    | O que fazer                                              |
| ---- | ----------------------- | -------------------------- | -------------------------------------------------------------------------------- | -------------------------------------------------------- |
| 400  | `invalid_request_error` | `missing_subject`          | `subject.doc` ou `subject.type` ausente                                          | Verificar o body da requisição                           |
| 400  | `invalid_request_error` | `invalid_document`         | CPF/CNPJ com quantidade de dígitos incorreta ou inconsistente com `subject.type` | Validar antes de enviar                                  |
| 400  | `invalid_request_error` | `invalid_subject_type`     | `subject.type` não é `pf` nem `pj`                                               | Verificar o campo                                        |
| 400  | `invalid_request_error` | `subject_type_mismatch`    | Template PJ, mas `subject.type: "pf"` (ou vice-versa)                            | Verificar o tipo do template                             |
| 400  | `invalid_request_error` | `missing_template_id`      | `template_id` ausente                                                            | Adicionar ao body                                        |
| 400  | `invalid_request_error` | `invalid_json`             | JSON malformado no body                                                          | Verificar o body                                         |
| 400  | `invalid_request_error` | `invalid_metadata`         | `metadata` não é objeto, tem mais de 10 chaves ou valores não-string             | Garantir objeto com até 10 chaves string                 |
| 400  | `invalid_request_error` | `invalid_webhook_url`      | `webhook_url` não é uma URL HTTPS válida                                         | Usar URL `https://`                                      |
| 400  | `invalid_request_error` | `invalid_cursor`           | Cursor de paginação corrompido                                                   | Usar cursor retornado pelo GET /runs                     |
| 400  | `invalid_request_error` | `unknown_api_version`      | Header `x-kycert-api-version` com valor desconhecido                             | Usar `2026-06-03` ou omitir o header                     |
| 401  | `authentication_error`  | `missing_api_key`          | Header `x-api-key` ausente                                                       | Adicionar o header                                       |
| 401  | `authentication_error`  | `invalid_api_key`          | Key revogada, inativa ou incorreta                                               | Verificar a key no dashboard                             |
| 402  | `billing_error`         | `quota_exceeded`           | Cota mensal de runs da chave esgotada                                            | Aumentar `runs_limit` no dashboard ou aguardar renovação |
| 402  | `billing_error`         | `billing_suspended`        | Saldo insuficiente ou conta suspensa                                             | Adicionar créditos no dashboard                          |
| 403  | `authorization_error`   | `insufficient_scope`       | Key sem o escopo necessário                                                      | Criar key com o escopo correto                           |
| 403  | `authorization_error`   | `subject_type_not_allowed` | Key restrita a PF, mas enviou PJ (ou vice-versa)                                 | Verificar escopos da key                                 |
| 404  | `invalid_request_error` | `template_not_found`       | `template_id` inativo, errado ou de outro tenant                                 | Verificar o ID no dashboard                              |
| 404  | `invalid_request_error` | `run_not_found`            | `run_id` inexistente ou de outro tenant                                          | Verificar o ID                                           |
| 429  | `invalid_request_error` | `rate_limit_exceeded`      | Cota de runs da chave atingida                                                   | Aguardar `Retry-After` segundos                          |
| 500  | `server_error`          | `internal_error`           | Falha interna                                                                    | Tentar novamente com backoff                             |
| 500  | `server_error`          | `run_not_created`          | Falha ao criar o run (erro interno)                                              | Tentar novamente com `Idempotency-Key`                   |
| 500  | `server_error`          | `run_failed`               | Falha ao iniciar o run                                                           | Tentar novamente                                         |

## Estratégia de retry para clientes da API

Para chamadas à API que falham com erros temporários, use backoff exponencial:

| Status                     | Reintentar? | Estratégia                                      |
| -------------------------- | ----------- | ----------------------------------------------- |
| `429` (cota atingida)      | Sim         | Aguardar `Retry-After` header (em segundos)     |
| `500`, `502`, `503`, `504` | Sim         | Backoff: 1s, 2s, 4s, 8s, 16s (máx 5 tentativas) |
| `400`                      | Não         | Erro de validação — corrigir o request          |
| `401`, `403`               | Não         | Erro de autenticação — verificar a API key      |
| `404`                      | Não         | Recurso não encontrado — não tente novamente    |

```typescript theme={null}
async function callWithRetry(fn: () => Promise<Response>, maxAttempts = 5) {
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
    const res = await fn()
    if (res.ok) return res
    if (res.status === 429) {
      const wait = parseInt(res.headers.get('Retry-After') ?? '60') * 1000
      await new Promise(r => setTimeout(r, wait))
      continue
    }
    if (res.status >= 500 && attempt < maxAttempts) {
      await new Promise(r => setTimeout(r, Math.pow(2, attempt) * 1000))
      continue
    }
    throw new Error(`API error: ${res.status}`)
  }
}
```

<Note>
  Use `Idempotency-Key` em `POST /runs` para garantir que retries não criem runs duplicados.
  Veja [Idempotência](/guias/idempotencia).
</Note>
