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

# Erros

> O envelope de erro da API do Pombo, os códigos de cada status HTTP e a ação sugerida para cada um.

Ramifique pelo **status HTTP** e por **`error_code`**. Nunca por `code`.

## Resumo

**Deu certo**

| Código | Significado                                                                                   |
| ------ | --------------------------------------------------------------------------------------------- |
| `200`  | Requisição atendida. Em `POST /notifications`, uma [repetição idempotente](/api/idempotencia) |
| `201`  | Recurso criado                                                                                |

**Algo na sua requisição precisa mudar**

| Código | Significado                                                                   |
| ------ | ----------------------------------------------------------------------------- |
| `400`  | [Requisição inválida](#400). É o status da maioria das recusas desta API      |
| `401`  | [Credencial](#401) ausente, inválida ou revogada                              |
| `402`  | [Saldo](#402) de envios insuficiente                                          |
| `404`  | [Recurso não encontrado](#404), inclusive quando pertence a outra organização |
| `409`  | [Conflito](#409) de contato, de chave idempotente ou de nome de modelo        |
| `413`  | [Mensagem grande demais](#413) para certificar                                |
| `422`  | [Recusado por regra de negócio](#422)                                         |
| `429`  | [Limite de requisições](#429) excedido                                        |

**Foi do nosso lado**

| Código | Significado                                     |
| ------ | ----------------------------------------------- |
| `500`  | [Erro interno](#500). Consulte antes de repetir |

<Note>
  **`403` não existe nesta API.** Um recurso de outra organização responde `404`, e não há modelo de
  permissão por credencial: toda credencial pode tudo o que a API oferece.
</Note>

## O envelope

Quatro campos, não dois.

```json theme={null}
{
  "code": 10400,
  "http_code": 400,
  "message": "Invalid request: Missing required fields: template_id",
  "error_code": "MISSING_REQUIRED_FIELD"
}
```

<ResponseField name="code" type="number | string">
  Identificador do erro. Numérico (`10000 + http_code`) na validação genérica, texto nos erros
  específicos. **Não ramifique por este campo.**
</ResponseField>

<ResponseField name="http_code" type="number">
  O status HTTP repetido no corpo. Presente quando o erro é devolvido diretamente, ausente quando é
  lançado e passa pelo tratador geral. Nunca exija este campo no cliente.
</ResponseField>

<ResponseField name="message" type="string" required>
  Descrição da falha, para diagnóstico. Não é estável e está em inglês ou em português.
</ResponseField>

<ResponseField name="error_code" type="string">
  O campo para ramificar, quando existe. **A maioria dos `400` não o traz:** ele marca as recusas que
  um cliente costuma querer tratar em código, não todas. Quando estiver ausente, use o status HTTP e
  leia a `message` para diagnóstico. A lista completa está logo abaixo.
</ResponseField>

<Note>
  **Por que não ramificar por `code`.** Duas operações vizinhas devolvem tipos diferentes no mesmo
  status: um `404` de `GET /templates/{id}` traz `10404`, e um de `GET /notifications/{id}` traz
  `"NOT_FOUND"`. Um `if (err.code === 10404)` funciona em uma e falha em silêncio na outra.
</Note>

## Os valores de error\_code

Sete valores, e é esta a lista inteira.

| `error_code`                               | Status | Onde aparece                                                                          |
| ------------------------------------------ | ------ | ------------------------------------------------------------------------------------- |
| `MISSING_REQUIRED_FIELD`                   | `400`  | Campo obrigatório faltando                                                            |
| `INVALID_RECIPIENT`                        | `400`  | Falta o campo de contato exigido pelo canal                                           |
| `INVALID_SENDER`                           | `400`  | Qualquer recusa do `sender`: ausente, incompleto ou com um dos campos fora do formato |
| `ORGANIZATION_ID_FORBIDDEN`                | `400`  | `organization_id` enviado no corpo, ou como parâmetro de consulta                     |
| `FILE_TRANSFER_NOT_AVAILABLE_ON_THIS_LANE` | `400`  | `file_transfer_id` enviado no envio                                                   |
| `TEMPLATE_CONSTANT_FORBIDDEN`              | `400`  | `language_code` ou `template_category_type` enviado em `POST /templates`              |
| `IDEMPOTENCY_KEY_REUSED`                   | `409`  | A chave já foi usada com outro corpo, ou com outro arquivo                            |

Todo o resto vem **sem `error_code`**, e aí o status HTTP é o que você tem. Em `400` isso inclui:
corpo JSON mal formado, `channel` fora dos dois canais, `attachment_id` enviado, `Idempotency-Key`
mal formada ou longa demais, telefone fora do formato, `recipient_tax_id_type` fora do enum,
`parameters` que não é objeto e todas as recusas de
`multipart/form-data`: ordem das partes, partes demais, tipo de arquivo e tamanho.

<Accordion title="Se você já tem código lendo code">
  O campo `code` carrega uma lista **diferente** de `error_code`, às vezes numérica (`10000 +
      http_code`) e às vezes em texto. Não ramifique por ele: o tipo muda entre operações vizinhas. Esta
  tabela existe só para quem já tem código lendo o campo.

  | `code` em texto                | Status |
  | ------------------------------ | ------ |
  | `INVALID_PARAMETERS`           | `400`  |
  | `NOT_FOUND`                    | `404`  |
  | `CONTACT_CONFLICT`             | `409`  |
  | `MESSAGE_TOO_LARGE_TO_CERTIFY` | `413`  |
  | `TEMPLATE_NOT_APPROVED`        | `422`  |
  | `SENDER_CHOICE_REQUIRED`       | `422`  |
  | `INSUFFICIENT_CREDITS`         | `402`  |
  | `RATE_LIMIT_EXCEEDED`          | `429`  |
  | `INTERNAL_ERROR`               | `500`  |
</Accordion>

## 400

**A validação para no primeiro erro, e a ordem é a desta tabela.** Ela importa porque tudo aqui
acontece antes da reserva de crédito: nenhum envio é descontado enquanto você acerta o formato. Num
`multipart/form-data`, as recusas de parte vêm antes de todas as outras, porque as partes são lidas
primeiro.

| `error_code`                               | `code`               | Quando acontece                                                                    | Ação sugerida                                                                                                       |
| ------------------------------------------ | -------------------- | ---------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
| ausente                                    | `10400`              | O corpo não é JSON válido, ou não é um objeto                                      | Corrija o JSON. A mensagem é `Invalid request: malformed JSON`                                                      |
| ausente                                    | `10400`              | `notification` ou `file` num corpo `application/json`                              | Para anexar, use `multipart/form-data`                                                                              |
| ausente                                    | `10400`              | `channel` diferente de `WHATSAPP` e `EMAIL`                                        | Use um dos dois canais                                                                                              |
| `FILE_TRANSFER_NOT_AVAILABLE_ON_THIS_LANE` | `10400`              | `file_transfer_id` enviado                                                         | Remova o campo. Envie o arquivo como a parte `file`                                                                 |
| ausente                                    | `10400`              | `attachment_id` enviado                                                            | Remova o campo. O arquivo viaja na própria chamada                                                                  |
| `ORGANIZATION_ID_FORBIDDEN`                | `10400`              | `organization_id` enviado                                                          | Remova o campo. A credencial diz a organização                                                                      |
| ausente                                    | `10400`              | `Idempotency-Key` com caracteres não aceitos, ou acima de 255 caracteres           | Use letras, dígitos, `_` e `-`                                                                                      |
| `MISSING_REQUIRED_FIELD`                   | `10400`              | Campo obrigatório faltando                                                         | Corrija a requisição. `sender` também é obrigatório, mas a sua ausência devolve `INVALID_SENDER`, não este código   |
| `INVALID_RECIPIENT`                        | `10400`              | `recipient_email` ausente em `EMAIL`, ou `recipient_phone` ausente no outro canal  | Envie o campo de contato do canal                                                                                   |
| ausente                                    | `10400`              | `recipient_email` sem `@`, ou telefone fora de `+CODIGO` com 10 a 15 dígitos       | Use `+5511999998888`                                                                                                |
| ausente                                    | `10400`              | `recipient_tax_id_type` diferente de `CPF` ou `CNPJ`                               | Corrija o tipo                                                                                                      |
| ausente                                    | `10400`              | `parameters` não é objeto                                                          | Envie `{}` se não houver variáveis                                                                                  |
| `INVALID_SENDER`                           | `10400`              | `sender` ausente, não é objeto, ou sem `name`, `tax_id` ou `tax_id_type`           | Envie os três campos                                                                                                |
| `INVALID_SENDER`                           | `10400`              | `sender.name` acima de 200 caracteres                                              | Encurte o nome. `sender.name must be at most 200 characters`                                                        |
| `INVALID_SENDER`                           | `10400`              | `sender.name` com `<`, `>`, `"` ou caractere de controle, quebra de linha incluída | Envie só texto simples. `sender.name must be plain text`                                                            |
| `INVALID_SENDER`                           | `10400`              | `sender.tax_id` reprovado no dígito verificador, ou de tipo diferente do declarado | Confira o documento e o `tax_id_type`. `sender.tax_id must be a valid CPF` ou `... CNPJ`                            |
| `INVALID_SENDER`                           | `10400`              | `sender.contact_phone` fora de `+CODIGO` com 10 a 15 dígitos                       | Use `+5511999998888`. `sender.contact_phone must be in format +CODE_PHONE with 10-15 digits (e.g., +5511999998888)` |
| ausente                                    | `INVALID_PARAMETERS` | Falta uma chave de `parameters`                                                    | Consulte `variables` em `GET /templates/{id}`                                                                       |
| ausente                                    | `INVALID_PARAMETERS` | Em `WHATSAPP`, o modelo é `DOCUMENT` ou `IMAGE` e o envio veio sem arquivo         | Envie como `multipart/form-data`, com a parte `file`                                                                |
| ausente                                    | `INVALID_PARAMETERS` | Em `WHATSAPP`, o modelo é `TEXT` e o envio veio com arquivo                        | Envie como `application/json`, ou use um modelo `DOCUMENT`                                                          |

As recusas de `multipart/form-data`, todas `10400` e todas sem `error_code`:

| Quando acontece                                                         | Ação sugerida                                      |
| ----------------------------------------------------------------------- | -------------------------------------------------- |
| A parte `notification` falta, ou veio depois da parte `file`            | Envie `notification` primeiro                      |
| Uma terceira parte qualquer                                             | São duas partes e só duas: `notification` e `file` |
| O arquivo passa de 20 MB (PDF) ou 5 MB (imagem)                         | Reduza o arquivo. Note que é `400`, não `413`      |
| O tipo do arquivo não é `application/pdf`, `image/png` nem `image/jpeg` | Converta o arquivo                                 |

<Note>
  `ORGANIZATION_ID_FORBIDDEN` tem duas redações. Em `POST /notifications`: `organization_id is not
      accepted on this endpoint: the API key decides the organization`. Em `POST /templates`:
  `organization_id must not be sent as a body field: it is taken from the API key`. Mesmo
  `error_code`.
</Note>

## 401

| Quando acontece                           | Ação sugerida                             |
| ----------------------------------------- | ----------------------------------------- |
| `Authorization` ausente ou mal formado    | Envie `Authorization: Bearer sk_live_...` |
| Segredo inválido, inexistente ou revogado | Gere uma nova credencial                  |

A resposta é idêntica nos três casos: o Pombo não informa qual deles ocorreu.

## 402

| `code`                            | Quando acontece        | Ação sugerida          |
| --------------------------------- | ---------------------- | ---------------------- |
| `INSUFFICIENT_CREDITS` ou `10402` | Sem envios disponíveis | Compre envios e repita |

Único erro que se resolve sem alterar a requisição.

## 404

| `code`                 | Quando acontece                        | Ação sugerida          |
| ---------------------- | -------------------------------------- | ---------------------- |
| `10404` ou `NOT_FOUND` | O identificador não existe             | Confira o valor        |
| `10404` ou `NOT_FOUND` | O recurso pertence a outra organização | Trate como inexistente |

Um `template_id` de outra organização em `POST /notifications` também responde `404`, e não `403`: a
distinção revelaria que o identificador existe.

```json theme={null}
{
  "code": 10404,
  "http_code": 404,
  "message": "Template not found"
}
```

<Note>
  Esse `404` vem direto da rota, então **não traz `request_id` no corpo**, ao contrário dos erros que
  passam pelo tratador geral. O cabeçalho `x-request-id` está sempre presente: use o cabeçalho.
</Note>

## 409

Três causas, e o corpo diz qual é.

| `error_code`             | `code`             | Quando acontece                                               | Ação sugerida                              |
| ------------------------ | ------------------ | ------------------------------------------------------------- | ------------------------------------------ |
| ausente                  | `CONTACT_CONFLICT` | Os dados do destinatário divergem de um contato já cadastrado | Reconcilie os dados                        |
| `IDEMPOTENCY_KEY_REUSED` | `10409`            | A mesma `Idempotency-Key` com um corpo diferente              | Use uma chave nova                         |
| ausente                  | `10409`            | Nome de modelo já usado, em `POST /templates`                 | Escolha outro nome. Veja a ressalva abaixo |

<Warning>
  **Um nome de modelo repetido quase nunca dá `409`.** O caminho normal é o Pombo renomear sozinho,
  acrescentando `-2`, `-3`, e devolver `201` com um **modelo novo**. Como a API não lista modelos,
  esse modelo fica inalcançável. Veja [Idempotência](/api/idempotencia).
</Warning>

**Conflito de contato.** Os contatos são unificados pelo `tax_id`, então o mesmo CPF com um telefone
diferente conflita em vez de criar uma segunda pessoa. O corpo traz campos extras para você
reconciliar.

```json theme={null}
{
  "code": "CONTACT_CONFLICT",
  "http_code": 409,
  "message": "Contato com este CPF já existe com um número de telefone diferente",
  "conflict_type": "tax_id_phone_mismatch",
  "existing_contact": {
    "contact_id": "PBD_CNT_01J9Z2Q8XK3M7WPTV6RB4CYH0N",
    "name": "Maria Silva",
    "tax_id": "12345678901",
    "phone": "+5511999998888"
  },
  "input_data": {
    "name": "Maria S. Silva",
    "phone": "+5511888887777",
    "tax_id": "12345678901",
    "tax_id_type": "CPF"
  }
}
```

<ResponseField name="conflict_type" type="string">
  `tax_id_phone_mismatch`, `phone_tax_id_mismatch` ou `name_mismatch`.
</ResponseField>

<ResponseField name="existing_contact" type="object">
  O contato já cadastrado.
</ResponseField>

<ResponseField name="input_data" type="object">
  Os dados enviados, para comparação.
</ResponseField>

Corrija a divergência antes de repetir. A mesma requisição devolve o mesmo conflito.

**Chave idempotente reutilizada.** Esse corpo vem **sem `http_code`**, porque é lançado e não
devolvido.

```json theme={null}
{
  "code": 10409,
  "message": "Idempotency-Key already used with a different request",
  "error_code": "IDEMPOTENCY_KEY_REUSED"
}
```

A chave já foi usada com outro corpo. Veja [Idempotência](/api/idempotencia).

## 413

| `code`                         | Quando acontece                                            | Ação sugerida                          |
| ------------------------------ | ---------------------------------------------------------- | -------------------------------------- |
| `MESSAGE_TOO_LARGE_TO_CERTIFY` | A **mensagem renderizada** excede o limite de certificação | Reduza o texto ou o HTML das variáveis |

Nada foi enviado e nada foi cobrado. A `message` informa o tamanho e o limite.

É o único `413` desta API. **Um anexo grande demais é `400`**, não `413`.

## 422

| `code`                   | Quando acontece                                                                                                   | Ação sugerida                                                       |
| ------------------------ | ----------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------- |
| `TEMPLATE_NOT_APPROVED`  | O modelo não está `APPROVED`                                                                                      | Aguarde a aprovação                                                 |
| `SENDER_CHOICE_REQUIRED` | A organização tem mais de um endereço de envio utilizável: os dedicados verificados mais o compartilhado do Pombo | Fale com o suporte do Pombo. Nenhuma chamada desta API resolve isso |
| `10422`                  | Conteúdo recusado pela política, em `POST /templates`                                                             | Revise o texto do modelo                                            |

O `10422` só acontece na criação do modelo, nunca no envio, e o corpo **não diz qual regra falhou**:
traz só `Content policy violation detected`.

## 429

| `code`                | Quando acontece                | Ação sugerida                                                |
| --------------------- | ------------------------------ | ------------------------------------------------------------ |
| `RATE_LIMIT_EXCEEDED` | Limite de requisições excedido | Aguarde e repita, veja [Limites de uso](/api/limites-de-uso) |

## 500

O corpo é reduzido e não traz `http_code`.

```json theme={null}
{
  "message": "Internal Server Error",
  "code": "INTERNAL_ERROR",
  "request_id": "req_01M1C5ZKGBGRNW6QHXTP2EM7Y7"
}
```

O `request_id` é o mesmo valor do cabeçalho `x-request-id`. Registre-o.

<Warning>
  Um `500` em `POST /notifications` não garante que nada foi enviado. Consulte antes de repetir: uma
  notificação extrajudicial emitida não pode ser cancelada.
</Warning>

## Depuração

Toda resposta traz o cabeçalho `x-request-id`.

```
x-request-id: req_01M1C5ZKGBGRNW6QHXTP2EM7Y7
```

**Registre esse valor junto com os seus próprios logs.** É por ele que o suporte encontra a
requisição exata, e sem ele a investigação começa por adivinhação.

Ao abrir um chamado, informe o `x-request-id`, o horário e o `notification_id`, quando houver.
