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.
{
"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.
Em 422 invalid_request, error.details.fields indica até 20 campos inválidos, com caminho, código e orientação. Os valores enviados não são devolvidos. O campo details é opcional: trate também erros que só tenham código e mensagem.
{
"success": false,
"error": {
"code": "invalid_request",
"message": "Dados inválidos. Revise os campos.",
"details": {
"fields": [
{ "field": "body.header_media.link", "code": "url_parsing", "message": "Informe uma URL válida." }
]
}
}
}Guarde o header X-Request-Id, o status HTTP e o código do erro junto à sua operação. Use essa referência para investigar uma chamada com o suporte; não registre o header Authorization nem códigos de autenticação.
O que fazer com cada resposta (link para esta seção)
| 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. |
Idempotency-Key, limitar tentativas e encaminhar casos persistentes para conferência.Encontrar o código de erro (link para esta seção)
Dados da requisição
| Código | Correção |
|---|---|
| invalid_request | Confira error.details.fields, tipos e limites no OpenAPI. Campos desconhecidos no envio são recusados; não são ignorados. |
| invalid_recipient | Use telefone internacional com DDI e DDD. Para um cadastro local, normalize antes com POST /validate e country; o envio não acrescenta 55. |
| 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. |
| invalid_template_status | Na consulta de templates, use APPROVED, ALL ou outro status documentado. Não envie um filtro vazio. |
Consulta e recuperação do webhook
| Código | Ação |
|---|---|
| invalid_status | Use pending, delivering, delivered ou failed ao consultar a fila do webhook. |
| invalid_event | No filtro de entregas, use um dos eventos documentados: message.status, message.received, contact.marketing_opt_out, template.status ou template.quality. |
| invalid_status_lookup | Na consulta GET /messages/status, informe client_ref ou o cabeçalho Idempotency-Key, nunca os dois nem um valor vazio. |
| ambiguous_client_ref | A referência foi reutilizada em mais de uma tentativa. Consulte pela chave de idempotência original ou pelo message_id; a API não escolhe o registro mais recente. |
| not_found | A entrega não existe nesta conta. Confira o identificador retornado pela listagem. |
| message_not_found | O envio não foi encontrado nesta conta. Confira message_id, eventos e retenção. Um 404 não comprova que a mensagem não foi enviada e não deve provocar reenvio automático. |
| not_retryable | A entrega não existe nesta conta ou não está em failed ou delivered. Confira o identificador e o estado antes de reenviar. |
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_not_allowed | O contato recusou promoções ou está bloqueado para marketing. Não repita o envio promocional. Avisos transacionais usam templates de utilidade adequados ao pedido. |
| marketing_experiment | A entrega de marketing foi limitada para esse destinatário. Não tente contornar a restrição com repetições. |
| marketing_recipient_limited | Meta 131049: marketing limitado para esse destinatário. Não comprova bloqueio do número da empresa. Evite repetir imediatamente. |
| delivery_limited | Meta 131048: entrega limitada por qualidade ou sinais de spam. Confira a situação do número e a qualidade dos envios na Meta. |
| 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.
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.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 (link para esta seção)
| Item | Limite |
|---|---|
| Requisições da API | 600 por minuto, por chave, somando todos os endpoints autenticados e todos os workers que usam essa chave. |
| 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. |
O teto é de requisições, não de mensagens: enviar, validar telefone, consultar janela, consultar templates e acompanhar a fila consomem a mesma cota. Enviar a cada 100 ms já ocupa 600 chamadas por minuto, antes dessas consultas e das novas tentativas.
Use um limitador compartilhado entre os workers, reserve margem para consultas e reaproveite o catálogo e a janela recebida no webhook. Ao receber 429 api_rate_limited, pause a fila dessa chave pelo Retry-After; quando informado, error.details.retry_after_seconds traz a mesma pausa em segundos.
Acompanhar a cota antes do 429
Respostas de chamadas autenticadas que passaram pelo contador incluem os cabeçalhos abaixo, tanto em sucesso quanto em recusas posteriores. Falhas anteriores à autenticação, respostas do proxy ou uma conexão interrompida podem não ter esses campos.
| Cabeçalho | Uso |
|---|---|
| X-RateLimit-Limit | Teto da janela de um minuto: 600 requisições por chave. |
| X-RateLimit-Remaining | Saldo imediatamente após contar esta chamada, nunca negativo. Chamadas concorrentes podem consumir o saldo antes da próxima requisição. |
| X-RateLimit-Reset | Instante de início da próxima janela, em segundos Unix UTC. Não é uma duração. |
| X-RateLimit-Scope | api_key: contador compartilhado. api_key_worker: contingência local quando o contador compartilhado está indisponível; mantenha seu próprio limitador global de 600/min. |
| Retry-After | Em 429 por cota, segundos a aguardar. A Meta também pode limitar uma chamada independentemente do saldo local. |
X-RateLimit-Limit: 600
X-RateLimit-Remaining: 541
X-RateLimit-Reset: 1793966460
X-RateLimit-Scope: api_keyO saldo é uma fotografia, não uma reserva para disparar esse volume imediatamente. Somar leituras e envios na mesma fila e manter margem evita que uma consulta em lote concorra com a entrega de mensagens transacionais.
Exemplo de novas tentativas (link para esta seção)
Este exemplo trata uma operação: repete condições temporárias conhecidas, falhas de conexão e 5xx, mantendo corpo e chave, respeitando Retry-After e encerrando após quatro tentativas. Ele não substitui o limitador compartilhado da fila. Recusas desconhecidas não entram em retry automático.
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.");| 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.