Erros e limites
Erro sempre volta com success: false e um objeto error. Trate pelo code, que é contrato e não muda. A message é para humano ler e pode mudar a qualquer momento.
formato
{
"success": false,
"error": {
"code": "outside_24h_window",
"message": "Faz mais de 24h que essa pessoa não escreve. Envie um template aprovado."
}
}Códigos (link para esta seção)
A coluna Repetir é a que importa na hora de escrever o seu retry: repetir o que está marcado com "não" só gasta a sua cota e o limite do seu número na Meta.
| code | Repetir? | O que fazer |
|---|---|---|
| invalid_recipient | não | Número fora do formato. Use só dígitos, com país e DDD. |
| empty_message | não | Faltou text ou template no corpo. |
| outside_24h_window | não | Passou da janela de 24h. Reenvie por template aprovado. |
| invalid_template | não | Template não existe, não está aprovado, ou faltou variável. |
| recipient_unreachable | não | O número não tem WhatsApp ou não pode receber. |
| no_connected_number | não | Nenhum número conectado na conta. Conecte no painel. |
| invalid_key | não | Chave inválida ou revogada. Crie outra no painel. |
| rate_limited | sim | Espere alguns segundos e tente de novo, com recuo progressivo. |
| delivery_limited | sim | Limite diário do seu número na Meta. Reduza o ritmo e volte depois. |
| account_restricted | depois | A conta está restrita na Meta. Resolva lá antes de insistir. |
| meta_unavailable | sim | Instabilidade na Meta. Tente de novo daqui a pouco. |
Códigos HTTP (link para esta seção)
| HTTP | Quando |
|---|---|
| 200 | Deu certo. Sempre confira success no corpo mesmo assim. |
| 400 | Corpo inválido (número torto, campo faltando). |
| 401 | Chave ausente, inválida ou revogada. |
| 404 | Recurso não existe (ex.: entrega de outro id). |
| 422 | A Meta recusou o envio (fora da janela, template pendente). |
| 429 | Passou do limite de requisições. |
Não devolvemos
5xx em erro de negócio de propósito. O que fica na frente da API substitui corpo de 5xx por uma página genérica, e aí você perderia justamente o code que explica o que houve.Limites (link para esta seção)
| Limite | Valor |
|---|---|
| Requisições | 600 por minuto, por CHAVE. Uma chave não atrapalha a outra. |
| Tamanho do texto | 4096 caracteres. |
| Variáveis por template | 20, de 512 caracteres cada. |
| Idempotency-Key | Guardada por 24 horas. |
| Entregas por página | 200 no máximo. |
A Meta tem os limites dela por cima: quantidade de contatos diferentes por dia, que varia com a qualidade do seu número. Isso aparece no painel, em Configurações. Estourar esse limite volta como delivery_limited.
Como escrever o seu retry (link para esta seção)
Recuo progressivo com um pouco de aleatoriedade, e teto de tentativas. Sem a parte aleatória, mil mensagens que falharam juntas voltam juntas e derrubam de novo o que estava se recuperando.
Node.js
const NAO_REPETIR = new Set([
"invalid_recipient", "empty_message", "outside_24h_window",
"invalid_template", "recipient_unreachable", "no_connected_number", "invalid_key",
]);
async function enviarComRetry(corpo, tentativa = 1) {
const r = await enviar(corpo);
if (r.success) return r;
if (NAO_REPETIR.has(r.error.code) || tentativa >= 5) return r;
// 2s, 4s, 8s, 16s, mais até 1s de folga aleatória para não voltarem todos juntos.
const espera = 2 ** tentativa * 1000 + Math.random() * 1000;
await new Promise((ok) => setTimeout(ok, espera));
return enviarComRetry(corpo, tentativa + 1);
}