# Erros e limites

Use o status HTTP e error.code para decidir entre corrigir os dados, aguardar ou verificar uma operação sem confirmação.

JSON · exemplo de recusa

```json
{
  "success": false,
  "error": {
    "code": "outside_24h_window",
    "message": "Faz mais de 24h que essa pessoa não escreve. Envie um template aprovado."
  }
}
```

`error.code` identifica a condição para sua aplicação. `error.message` explica o problema para uma pessoa e pode mudar sem alterar o contrato.

## O que fazer com cada resposta

| HTTP | Interpretação e ação |
| --- | --- |
| 200 | Leia data. No envio, sent significa aceite; confirme entrega pelo webhook. |
| 400 / 422 | Corrija os campos ou a regra indicada em error.code. 422 também cobre validações locais, não apenas recusas da Meta. |
| 401 | Confira o header Authorization e se a chave foi revogada. |
| 404 | Confira o identificador e a conta à qual o recurso pertence. |
| 409 | Confira a operação original. Não troque a chave para contornar idempotência. |
| 429 | Reduza o ritmo e respeite Retry-After quando informado. |
| 5xx / falha de conexão | O resultado pode estar incerto. Preserve corpo e chave ao tentar novamente. |

> Atenção: Uma resposta sem confirmação não prova que a mensagem deixou de ser enviada. A sua política de retry deve manter a mesma `Idempotency-Key`, limitar tentativas e encaminhar casos persistentes para conferência.

## Encontrar o código de erro

### Dados da requisição

| Código | Correção |
| --- | --- |
| invalid\_request | Confira tipos, campos obrigatórios e limites no OpenAPI. |
| invalid\_recipient | Use telefone com país e DDD, somente dígitos. |
| empty\_message / ambiguous\_message | Informe text ou template. Não deixe ambos ausentes nem envie os dois juntos. |
| invalid\_idempotency\_key | Informe uma chave não vazia, de até 64 caracteres. |
| variable\_too\_long | Reduza a variável ao limite de 1.024 caracteres, sem perder dados essenciais. |
| variable\_bad\_format / template\_param\_format | Remova quebras de linha, tabulações e sequências de mais de quatro espaços das variáveis. |
| invalid\_pix\_order / pix\_needs\_template | Confira o template de cobrança, os campos PIX e a soma dos itens em centavos. |

### Template e conteúdo

| Código | Correção |
| --- | --- |
| outside\_24h\_window | Use template aprovado quando a janela de texto livre estiver fechada. |
| template\_not\_found | Confira nome, idioma e número conectado. |
| invalid\_template / template\_paused | Confira aprovação, qualidade e disponibilidade do modelo na Meta. |
| template\_format\_unsupported | Consulte api\_supported e api\_unsupported\_reason e escolha um modelo compatível. |
| template\_params\_mismatch | Confira quantidade e nomes das variáveis, cabeçalho e posição dos botões. |
| template\_required\_header\_media | Envie o arquivo exigido em header\_media ou use o modelo sem anexo. |
| template\_header\_mismatch | Use o tipo de mídia ou texto de cabeçalho exigido pelo modelo. |
| invalid\_otp | Confira código, categoria AUTHENTICATION e botão no índice 0. Não use campos de cupom. |

### Acesso, destinatário e conta

| Código | Ação |
| --- | --- |
| missing\_api\_key / invalid\_api\_key | Confira a chave no servidor e o header Authorization. Revogue a credencial se houver exposição. |
| no\_connected\_number | Confira o número selecionado e a conexão da conta no painel. |
| recipient\_unreachable | A mensagem não pôde ser entregue. O código Meta 131026 não confirma ausência de WhatsApp. Confira o telefone com o cliente antes de reenviar. |
| marketing\_experiment | A entrega de marketing foi limitada para esse destinatário. Não tente contornar a restrição com repetições. |
| delivery\_limited | A Meta limitou a entrega por qualidade ou engajamento. Confira o número e o destinatário antes de outro envio. |
| account\_restricted | Confira e resolva a restrição da conta na Meta antes de repetir. |
| payment\_issue | Confira a elegibilidade de pagamento e a configuração de cobrança na Meta. O código 131042 não comprova saldo insuficiente. |

Uma mensagem aceita para processamento ainda pode falhar por pagamento. Confira o status pelo webhook antes de reenviar; se houver cobrança pré-paga, verifique também o saldo no painel da Meta. A modalidade de cobrança não muda o endpoint de envio.

> O erro `131026` pode ocorrer por número sem cadastro, aplicativo desatualizado ou termos ainda não aceitos pelo destinatário. A Meta não distingue essas causas nesse código. Peça ao cliente, por outro canal, que confirme o telefone e tente iniciar uma conversa com o número da empresa. Consulte os [códigos de erro da Meta](https://developers.facebook.com/documentation/business-messaging/whatsapp/support/error-codes).

### Operação em andamento ou indisponibilidade

| Código | Nova tentativa |
| --- | --- |
| idempotency\_in\_flight | Aguarde e use a mesma chave e corpo. Se continuar sem confirmação, confira os eventos. |
| idempotency\_conflict | Não repita automaticamente: a chave pertence a outro conteúdo. |
| idempotency\_legacy\_unverified | Confira o registro antigo e os eventos; a API não pode comparar o conteúdo. |
| api\_rate\_limited | Respeite o teto por chave e Retry-After quando informado. |
| rate\_limited | Reduza o ritmo; este limite foi informado pela Meta. |
| templates\_unavailable / meta\_unavailable / db\_unavailable | Aguarde e use a mesma chave e conteúdo, com limite de tentativas. |
| network\_error / send\_failed | Preserve a operação, confira os status e investigue antes de iniciar outro envio. |

## Limites da integração

| Item | Limite |
| --- | --- |
| Requisições da API | 600 por minuto, por chave. Os limites da Meta continuam aplicáveis. |
| Idempotency-Key | Até 64 caracteres. Retenção padrão de dois dias; não substitui seu histórico permanente de operações. |
| Texto livre | Até 4.096 caracteres. |
| Variáveis de template | Até 20 valores de 1.024 caracteres. Valores maiores são recusados, não cortados. |
| Código OTP / cupom | Até 15 caracteres. São campos e tipos de botão diferentes. |
| Consulta de entregas | Até 200 itens por página, com paginação por cursor. |

## Exemplo de novas tentativas

Este exemplo repete condições temporárias conhecidas, falhas de conexão e 5xx. Mantém corpo e chave, respeita `Retry-After` e encerra após quatro tentativas. Recusas desconhecidas não entram em retry automático.

Node.js · retry com a mesma operação

```javascript
const TEMPORARIOS = new Set([
  "api_rate_limited", "rate_limited", "templates_unavailable",
  "meta_unavailable", "db_unavailable", "idempotency_in_flight",
]);
const esperar = (ms) => new Promise((ok) => setTimeout(ok, ms));

async function enviarComRetry(operacao) {
  const token = process.env.PT_API_KEY;
  const chave = operacao.chave;
  if (!token || !chave) throw new Error("Configure a chave de API e a chave da operação.");
  // Salve a operação antes de chamar. Não gere uma chave nova dentro deste laço.
  const corpo = JSON.stringify(operacao.corpo);
  for (let tentativa = 1; tentativa <= 4; tentativa++) {
    let resposta = null;
    let dados = null;
    try {
      resposta = await fetch("https://api.patrociniotech.com/public/v1/messages", {
        method: "POST",
        signal: AbortSignal.timeout(15000),
        headers: {
          Authorization: `Bearer ${token}`,
          "Content-Type": "application/json",
          "Idempotency-Key": chave,
        },
        body: corpo,
      });
      dados = await resposta.json().catch(() => null);
    } catch {
      // Timeout não comprova que a mensagem foi recusada.
    }
    if (resposta?.ok && dados?.success === true && dados?.data?.message_id) {
      return { estado: "aceita", dados: dados.data };
    }
    const codigo = typeof dados?.error?.code === "string" ? dados.error.code : null;
    const temporario = !resposta || resposta.status >= 500 || resposta.status === 429
      || (resposta.ok && dados?.success !== false) || TEMPORARIOS.has(codigo);
    if (!temporario) {
      const verificar = !codigo || codigo.startsWith("idempotency_");
      return { estado: verificar ? "verificar" : "recusada", http: resposta.status, erro: dados?.error };
    }
    if (tentativa === 4) break;

    const header = resposta?.headers.get("Retry-After");
    const segundos = header ? Number(header) : NaN;
    const prazo = Number.isFinite(segundos) ? segundos * 1000 : Date.parse(header || "") - Date.now();
    const recuo = 2 ** tentativa * 1000 + Math.random() * 1000;
    await esperar(Math.max(recuo, Number.isFinite(prazo) ? prazo : 0));
  }
  return { estado: "verificar", motivo: "Confira os eventos e preserve a mesma operação." };
}
```

| estado retornado pelo exemplo | Tratamento no seu sistema |
| --- | --- |
| aceita | Guarde o message\_id. Aguarde o webhook para confirmar entrega. |
| recusada | Mostre o motivo e corrija os dados ou a configuração antes de tentar novamente. |
| verificar | Mantenha a operação pendente de conferência. Consulte eventos; não crie outra chave automaticamente. |

Execute em um worker e persista a operação antes de enviar. Para pausas longas, reagende na sua fila. Uma resposta de infraestrutura pode não ser JSON; o exemplo trata essa condição sem assumir sucesso.

Para a conciliação completa, veja [envio e processamento do webhook](https://patrociniotech.com/documentacao-api/exemplo.md).

---

Fonte: https://patrociniotech.com/documentacao-api/erros
Índice para IA: https://patrociniotech.com/documentacao-api/llms.txt
