Erros que dizem o que fazer
Todo erro é um envelope JSON com um código estável, uma mensagem que diz como resolver, e Retry-After em tudo que se resolve esperando. Esta página é a tabela que a sua lógica de retentativa precisa.
O formato
Retry-After: 42
{
"error": {
"code": "QUOTA_EXCEEDED",
"message": "Daily send limit reached (20/day on Free). It clears at midnight, or Starter sends 60/day",
"messageKey": "serverErrors.dailyCapReached",
"details": { "scope": "day", "limit": 20, "resetAt": "2026-08-25T03:00:00.000Z", "retryAfterSecs": 42 }
}
}messageKey é estável, pra você traduzir do seu jeito. message é o texto em inglês, de reserva. As recusas que pedem upgrade dizem qual é o plano mais barato que resolve, porque erro sem saída não é mensagem de erro.
Todos os códigos
| 400 VALIDATION_ERROR | Algum campo veio errado. `details` lista caminhos e regras, nunca os seus valores. |
| 401 UNAUTHORIZED / INVALID_TOKEN | A chave está faltando, errada ou revogada. Mande `Authorization: Bearer wa_…`. |
| 402 PAYMENT_REQUIRED | Um limite que só compra levanta, como mais números ou envio em lote. Esperar não resolve. |
| 403 FORBIDDEN | A conta está suspensa. |
| 404 NOT_FOUND | Esse recurso não existe, ou não é seu. |
| 409 NUMBER_NOT_CONNECTED | O número não tem uma sessão do WhatsApp que permita enviar. Abra Números, confira o WhatsApp no celular e reconecte. Tentar o envio de novo agora não resolve. |
| 409 CONFLICT | A requisição precisa de uma ação ou já está nesse estado. Um número restrito pede `recoveryConfirmed: true`, só depois de você conferir que o WhatsApp funciona no celular. Uma requisição com a mesma `Idempotency-Key` pode ainda estar rodando: espere o `Retry-After` e repita com a mesma chave. |
| 422 IDEMPOTENCY_KEY_REUSED | Essa `Idempotency-Key` já foi usada em outra requisição. Mande uma nova. Esperar não resolve. |
| 429 RATE_LIMIT_EXCEEDED | Rápido demais. `Retry-After` diz exatamente quanto esperar. |
| 429 QUOTA_EXCEEDED | Limite diário, cota mensal, ou o limite de conversas novas de um número. `details.scope` diz qual: `day`, `month` ou `newConversations`. `resetAt` diz quando libera. |
| 429 PAIRING_COOLDOWN | Pedidos demais de código de pareamento para este número. `Retry-After` diz quando você pode pedir outro. Isso não é um tempo fixo de restrição. |
| 502 GATEWAY_ERROR | O WhatsApp recusou lá em cima. Tentar de novo pode funcionar. |
| 504 GATEWAY_TIMEOUT | O WhatsApp não confirmou a tempo. Confira a conversa e, se vier `details.messageId`, acompanhe esse id no webhook `message.status` antes de enviar de novo. Uma nova tentativa com a mesma `Idempotency-Key` recebe a mesma resposta, sem um segundo envio. |
| 503 SERVICE_UNAVAILABLE | Uma dependência caiu. Tente de novo em instantes. |
Quando o número cai
O celular pode perder a conexão, e uma conta nova pode ainda não ter número. Novos envios respondem 409 NUMBER_NOT_CONNECTED sem cobrar nada. As mensagens já na fila que ainda não saíram pausam até o número reconectar. Elas não viram falhas só porque ele caiu, e voltam a sair com o intervalo configurado. message.failed indica um envio que não pode continuar ou cujo resultado não é conhecido, não uma fila pausada.
- Tenha um segundo canal. O que alguém precisa receber vai também por e-mail, ou por e-mail quando o WhatsApp recusar.
- Antes de oferecer algo que só funciona pelo WhatsApp, como um código de login, confira se tem número conectado. Guarde a resposta por um minuto e jogue fora quando um envio voltar
NUMBER_NOT_CONNECTED. - Sem número conectado, esconda essa opção e mostre o caminho pelo e-mail. Não mostre um botão que só pode falhar.
- Se você tem webhook, o
number.disconnectedchega quando o número continua fora depois de 15 minutos de tentativas, e onumber.connectedquando ele volta.
import { Whatsapi } from "@whatsapi.sh/sdk";
const wa = new Whatsapi({ apiKey: process.env.WHATSAPI_KEY! });
const numbers = await wa.listNumbers();
const whatsappReady = numbers.some((n) => n.status === "connected");Regras para tentar de novo
Respeite Retry-After num 429. Nunca repita um 402 automaticamente, porque ele se resolve com compra, não com espera. Num 504, não reenvie no escuro: o id da mensagem em details vai aparecer no seu webhook message.status se ela chegou, e reenviar sem conferir é como uma pessoa recebe mensagem duas vezes. Para repetir um envio sem risco, mande com o header Idempotency-Key e repita com o mesmo valor: você recebe a primeira resposta em vez de uma segunda mensagem. Os limites de taxa são proteção, não burocracia, e como não ser banido explica como ficar embaixo deles desde o começo.