Ir para o conteúdo
Documentação Ir para o painel
Erros

Quando dá erro

Antes de procurar o código, descubra de onde ele veio. São duas tabelas nesta página, e olhar na errada é meia hora perdida. E existe um terceiro caso, o pior de todos, em que nada dá erro e nada chega: ele tem seção própria aqui embaixo.

Esta lista não é exaustiva, e isso é declarado de propósito.

Só entra aqui o código que a Zera leu no próprio código ou mediu numa chamada real. A coluna onde a Zera viu isto traz a fonte de cada linha. Código que nós conhecemos de ouvir falar fica de fora: uma frase inventada nesta página vira uma hora perdida do seu lado.

De onde veio o erro

São três origens, e cada uma tem um formato diferente. Reconhecer o formato já resolve metade.

As três origens de erro, como cada uma chega e onde procurar
Quem respondeuComo o erro chegaOnde procurar
A Graph da Meta Um objeto error com code numérico, dentro do corpo. Você estava chamando graph.facebook.com direto, com o seu token. A tabela Os erros da Meta, aqui embaixo.
O painel da Zera { "ok": false, "erro": "..." }, com uma chave em português. Veio de uma rota /api/..., ou seja, de dentro do painel. A tabela As recusas do painel.
A janela de conexão Uma frase em inglês numa janela do Facebook, na hora de conectar o número. Não tem código nem JSON. O que cada mensagem quer dizer, que é a página dessa etapa.

Mandei e não chegou

É o caso mais comum de chegar até aqui, e quase sempre ele não é erro nenhum. Percorra nesta ordem, e pare no primeiro que responder não.

  1. A chamada voltou 200 com um wamid? Se não voltou, o erro está no corpo da resposta, e o código dele está na tabela da Meta. Vá para ela.
  2. Voltou. Então a Meta aceitou, e o problema não está na sua chamada. "message_status": "accepted" quer dizer aceita, nunca entregue.
  3. A janela de 24 horas está aberta? Texto livre só chega para quem escreveu para o seu número nas últimas 24 horas. Fora dela, o caminho é modelo aprovado. Detalhe em A janela de 24 horas e em Modelos de mensagem.
  4. O destino está em E.164 sem o sinal de mais? Um celular de São Paulo fica 5511987654321. Sem espaço, sem parêntese e sem traço.
  5. Você está esperando a resposta da pessoa, e não a sua ida? Então leia O erro que não dá erro antes de continuar depurando. É o caso em que o seu código está certo e a entrega nunca foi apontada para você.

O formato do erro da Meta

A Meta devolve o erro dentro do corpo, e não só no status. Estes são os campos que o produto da Zera lê de verdade quando trata uma falha da Graph.

O corpo de um erro da Graph
{
  "error": {
    "message": "...",
    "type": "OAuthException",
    "code": 190,
    "error_subcode": ...,
    "error_data": { "details": "..." },
    "fbtrace_id": "..."
  }
}
  • Decida pelo code, nunca pelo message. É regra escrita no nosso executor, e ela veio da própria documentação da Meta, que avisa para não construir lógica em cima do texto. Um if que procura palavra dentro da frase quebra no dia em que a Meta reescrever a frase, e quebra em silêncio.
  • A mesma frase junta causas diferentes. A Graph responde does not exist, cannot be loaded due to missing permissions, or does not support this operation para três situações distintas. Quem lê a frase acerta o caso inofensivo e engole o perigoso.
  • error_data.details é onde mora o tempo de espera. Quando a Meta manda esperar, ela diz quanto ali dentro, em texto livre. Não crave um número no seu código: leia o que veio.
  • error_subcode aparece só em parte dos erros, e ele afina o code. O valor está com reticências acima de propósito: cravar um número ali seria um exemplo que ninguém mediu. Um subcódigo que a Zera mediu é o 33, que vem junto do code 100 quando o identificador do caminho não existe.
  • fbtrace_id é o que o suporte da Meta pede. Guarde no seu registro, junto com o horário.

Os erros da Meta

Estes são os códigos que a Zera trata no próprio código ou mediu em chamada real. Cada linha diz onde a Zera viu.

Códigos de erro da Graph que a Zera trata ou mediu
CódigoO que quer dizerO que fazerOnde a Zera viu isto
190 A autorização não vale mais. Token errado, vencido ou revogado dentro da Meta. Reconecte o número no painel. A Zera não gera outra credencial e não revoga a atual: só a Meta faz as duas coisas. _saude-meta.js, conjunto de revogação. _es-executor.js, passo que lê a conta. Medido: token ruim numa rota real devolve 190.
200 O token existe e não alcança o que você pediu. É falta de permissão, não de validade. Reconecte o número. Se ele aparecer só no envio, o suspeito é o escopo de mensagens: leia O escopo que ninguém confere. _saude-meta.js, mesmo conjunto do 190. _chatwoot.js: no modelo de provedora de tecnologia a conta é do cliente, e um token da Zera toma erro 200 nela.
100 Campo ou parâmetro que não existe nessa versão. Um único campo desconhecido no fields derruba a resposta inteira, e não só aquele campo. Peça poucos campos por chamada e vá tirando um a um. Parâmetro a mais também cai aqui, sem dizer qual sobrou. _saude-meta.js: é o motivo de a leitura de saúde ser três chamadas separadas. _es-executor.js, passo do OAuth. Medido em v25.0: GET /{waba_id}/phone_numbers sem token devolve 100, com Tried accessing nonexisting field (phone_numbers). Vale para borda de subrecurso, e não para toda chamada sem token: veja a linha do 104.
104 A chamada foi sem token, num nó que pede token para ser lido. A mensagem é An access token is required to request this resource. Mande o cabeçalho Authorization: Bearer. Se você acha que mandou, confira se o cliente HTTP não está descartando o cabeçalho num redirecionamento. Medido em v25.0: GET /{id}?fields=messaging_limit_tier sem token devolve 104. Este código não está em nenhuma lista do nosso executor, então ele cai no balde dos definitivos.
2500 Duas causas, e a mensagem separa as duas. Unknown path components quer dizer que sobrou segmento no caminho. An active access token must be used quer dizer que a chamada foi sem token para um nó que depende do usuário corrente, como /me. Leia a mensagem antes de mexer no caminho. Se ela falar de access token, o problema é a autorização, e conferir o caminho é meia hora perdida. Se falar de path components, confira a versão e o caminho, nesta ordem. Medido em v25.0: /{waba_id}/phone_numbers/extra devolve 2500 com Unknown path components: /extra, e GET /me sem token devolve 2500 com a mensagem de token. Um identificador inventado, ao contrário, devolve 100 com error_subcode 33, e não 2500.
4, 613, 80007 Limite de chamadas. O 4 merece atenção: ele é o teto do aplicativo inteiro, e não da sua conta. Espere e reduza a frequência. Não repita em laço: insistir num limite consome o orçamento sem destravar nada. _saude-meta.js, conjunto de limite de chamadas. _es-executor.js traz a nota sobre o 4 ser do aplicativo inteiro.
2 Indisponibilidade passageira do lado da Meta. Tente de novo daqui a pouco. Uma vez, não em laço. _es-executor.js, conjunto de retentáveis, com a citação da própria Meta.
368 Conta restrita por violação de política. Resolva no Gerenciador da Meta. Repetir a chamada não destrava, e a documentação da Meta se contradiz sobre este código: no contexto de conta de WhatsApp vale a leitura de restrição. _es-executor.js, conjunto de definitivos, com o comentário sobre a contradição. admin/desligar.js.
3, 10 A Zera não traduz cada um. O produto os põe no mesmo balde do 190, do 200 e do 368: credencial morta. Nenhuma tentativa nossa resolve. Quem resolve é o dono da conta, dentro da Meta. admin/desligar.js: a lista [190, 200, 368, 10, 3], com a frase só o cliente consegue remover a inscrição.
133008, 133009, 133016 A Meta mandou esperar, e diz quanto dentro de error_data.details. Leia o tempo que veio e espere exatamente ele. Chutar um número esperando de menos queima tentativa do orçamento da Meta. _es-executor.js, conjunto de espera com prazo, com o [HARD] proibindo cravar o tempo no código.

Os outros que o produto classifica sem traduzir

O executor da Zera separa mais alguns códigos em duas caixas, para decidir se tenta de novo. Ele não traduz cada um, e esta página também não vai inventar a frase de nenhum deles. O que dá para dizer com honestidade é a caixa em que cada um caiu.

  • Tratados como passageiros: 1, 17, 341, 80008, 131000, 133004 e 133015. Tentar de novo daqui a pouco tem chance.
  • Tratados como definitivos: 131031, 133005, 133006 e 133010. Insistir não destrava e ainda gasta chamada.
  • Código que não está em nenhuma das listas também é tratado como definitivo, e isso é escolha, não descuido. Insistir num erro que ninguém entende, num endereço que cria conta de verdade na Meta, é como se descobre um limite depois de queimar todas as tentativas dele.

Se você bater num código que não está nesta página, a autoridade é a referência de erros da Meta. E escreva para a Zera: a sua medição vira uma linha nova aqui.

As recusas do painel

Estas são as respostas das rotas /api/... do painel. Elas não são uma API pública da Zera: não existe chave da Zera, não existe endereço da Zera no caminho do seu envio, e o que você faz com a credencial vai direto para a Meta. Elas aparecem quando o painel recusa alguma coisa na sua frente, e a chave é sempre a mesma palavra.

O formato da recusa do painel
{ "ok": false, "erro": "sessao_velha" }

Algumas respostas trazem um campo a mais, e ele é útil: teto_por_dia diz qual é o limite que você encostou, proxima_em diz a partir de quando dá para tentar de novo, e lido_em diz de quando é o valor que continua na tela.

proxima_em e lido_em vêm em segundos desde 1970, e não em milissegundos. Em JavaScript, comparar com Date.now() direto dá uma espera de dezenas de milhares de anos, ou de zero, e não levanta erro nenhum. Compare com Math.floor(Date.now() / 1000).

As recusas das rotas do painel da Zera, com o status HTTP de cada uma
ChaveHTTPO que quer dizerO que fazer
sessao 401 A sua sessão não foi reconhecida. Peça um link de acesso novo pelo e-mail do cadastro.
sessao_velha 403 Duas ações exigem um acesso recém-feito, com menos de dez minutos de vida: exibir a credencial e salvar o endereço de webhook. Estar logado não basta. O salvamento entrou nessa lista em 23 de setembro de 2026, quando a escrita na Meta foi ligada em produção: antes disso ele só gravava uma coluna, e agora ele redireciona o recebimento do número. A trava existe no código desde 15 de setembro, e só vale com a escrita ligada. Peça um link novo por e-mail, ou entre de novo com a senha. As duas ações ficam liberadas pelos dez minutos seguintes.
origem 403 O pedido de escrita se identificou como vindo de outra origem que não a do painel. Use o painel. A guarda olha os cabeçalhos Sec-Fetch-Site e Origin, e ela falha fechada num caso específico: quando o pedido diz de onde veio e nós não conseguimos determinar qual é a nossa própria origem, ela recusa. Quem segura um pedido que não se identifica é o cookie de sessão, que esta rota exige do mesmo jeito.
so_o_dono 403 Quem entrou por convite recebe esta recusa, mesmo com acesso ao painel e mesmo com uma sessão recém-feita, em dois lugares que pegam quem integra: ao exibir a credencial e ao salvar o endereço do webhook. No salvamento, ela vem antes das travas próprias dele (endereço, plano, teto, sessão recém-feita e aviso ao dono): nada é gravado, nenhum e-mail sai e a Meta não é chamada. O bloco do webhook aparece para o convidado do mesmo jeito, então a recusa só chega quando ele clica em salvar. Peça ao dono da conta que exiba a credencial e salve o endereço. É a trava que mais pega desenvolvedor, porque o normal é o cliente convidar quem vai integrar. Os detalhes estão em A sua credencial e em Receber mensagens.
plano_com_inbox 409 O seu plano inclui a caixa de entrada da Zera, e o recebimento deste número já é dela. Hoje isto é recusa do salvamento do endereço de webhook, e não da exibição da credencial: desde 06 de setembro de 2026 a credencial é exibida em todo plano. Nada. Quem atende pela plataforma da Zera já recebe as mensagens por ela, e um segundo endereço seria um campo que ninguém lê. A credencial continua saindo normalmente, e ela serve para o seu sistema enviar.
webhook_meta 502 Só existe ao apagar o endereço, salvando em branco: a Meta recusou apagar, ou o e-mail de aviso ao dono da conta não foi aceito pelo provedor e a Meta não chegou a ser tocada, de propósito. Nada foi apagado. O corpo traz um objeto meta com estado, erro, em e pnid. Ao apontar, este código não aparece, nem quando a Meta recusa: a resposta é HTTP 200 com "ok": true, e quem diz o que aconteceu é o campo meta. meta.estado igual a "erro" é tentativa que não fechou. A ausência do campo meta é o sinal de que nenhuma tentativa na Meta aconteceu: o e-mail de aviso não foi aceito, o número não concluiu a conexão, ou a escrita está desligada do nosso lado. Olhe o campo meta, e não o status. Em meta.erro: falha_nossa é do nosso lado, e nem todo caminho que leva a ele gera alerta, então escreva para a Zera se ele continuar depois de alguns minutos; endereco_longo quer dizer endereço acima de 200 caracteres, recusado por nós antes de a Meta ser chamada; retentavel:0 quer dizer que a Meta não respondeu, ou respondeu com uma falha do lado dela, e não que ela recusou; divergente: quer dizer que a Meta está entregando em outro lugar; ambiente: e orcamento:, sempre com código negativo, são nossos; qualquer outro classe:código quer dizer que a Meta não confirmou o endereço, e o erro pode ter vindo da releitura, depois de uma escrita que valeu, ou ser limite de chamadas (espera): trate como não confirmado, e não como recusado. Se o código for definitivo:190 ou definitivo:200, a autorização do número deixou de valer na Meta: salvar de novo não resolve e manda outro e-mail ao dono a cada tentativa. Reconecte o número e só então salve o endereço de novo. Sem meta, trate o 200 como não apontado. Tente de novo, e se insistir escreva para a Zera. O detalhe está em O que a resposta do salvamento diz.
sem_waba 409 Este número ainda não passou pela conexão com a Meta, então ainda não existe credencial para ele. Conecte o número. A credencial é criada pela Meta nesse momento.
sem_token 409 O número foi conectado antes de a Zera passar a guardar a autorização. Reconecte o número.
sem_numero 409 A conta existe e o número ainda não foi registrado na Meta, então não há o que ler. Termine a conexão.
token_vencido 409 A credencial guardada já venceu. Ela não é exibida de propósito. Reconecte. Entregar uma chave morta custaria meia hora sua depurando um problema que é da conexão.
autorizacao_revogada 409 A Meta respondeu 190 ou 200 na leitura. Reconecte o número. É a mesma história das duas primeiras linhas da tabela da Meta.
conta_cancelada 409 A conta está cancelada. Ela continua lendo o histórico e não escreve mais nada. Para voltar, refaça o cadastro com este mesmo e-mail.
webhook_cliente 400 O endereço de webhook foi recusado. Ele precisa ser uma URL completa, em https, com host, e com no máximo 200 caracteres, que é o teto da Meta para override_callback_uri. Corrija o endereço. Acima de 200 caracteres ele é recusado e não cortado, de propósito: um endereço cortado passaria calado e a Meta passaria a entregar num destino diferente do que está no seu servidor.
teto_do_dia 429 São três exibições de credencial por conta, e conta a tentativa; a que falha por problema nosso é devolvida. A janela é de 24 horas corridas, contadas a partir de cada exibição, e não da meia-noite. Espere a janela virar: o limite libera 24 horas depois da primeira das três exibições, e não à meia-noite. Se você precisa dela agora, escreva para a Zera.
teto_da_hora 429 Você encostou num teto por hora, por conta. O número muda com a rota: são vinte nas leituras que vão à Meta, trinta no documento de fatura, e dez nas duas escritas na Meta que usam este código: salvar o perfil do número e criar um modelo novo, cada uma com a sua contagem. Os vinte das leituras são contados em separado para cada uma das quatro (perfil, modelos, métricas e estado do número), e não somados num balde só: gastar as vinte de Modelos não tira nenhuma de Perfil. A troca do endereço de webhook também tem teto de dez por hora, mas responde com outro código, rate. Este teto é da Zera, e o da Meta continua existindo por baixo dele. Espere. Numa leitura, o que está na tela continua valendo, com a data de leitura ao lado. Numa escrita, nada foi enviado para a Meta: tente de novo depois que a hora virar.
muito_cedo 429 Duas leituras seguidas rápido demais. O estado do número tem freio de cinco minutos, e ele vale para toda leitura. Nas outras abas o freio é de um minuto e só é avaliado quando você pede atualização forçada: sem forçar, a resposta pode vir de um cache de até dez minutos, e nesse caso ela chega sem recusa nenhuma. Olhe o proxima_em da resposta e espere até lá. Para saber de quando é o que você está lendo, olhe o lido_em.
rate 429 Você encostou no teto por hora daquela rota. O número muda de rota para rota, então esta chave sozinha não diz qual limite você bateu: na troca de endereço de webhook são dez tentativas de salvar por hora, por conta, contando as recusadas pela trava da sessão, e nas rotas de cadastro, senha, entrada e pagamento os números são outros. Espere uma hora e repare no que você estava fazendo quando a recusa veio. Bater neste teto costuma ser laço de script, e não uso de gente.
meta_limitou 429 A Meta respondeu 4, 613 ou 80007 na leitura. Espere. Assim que ela liberar, a leitura volta sozinha.
nao_encontrado 404 O número não está nesta conta, ou o identificador está fora de formato. As duas respostas são idênticas de propósito: um 403 confirmaria que o identificador existe. Volte para a lista e abra um dos seus números.
campo_indisponivel 502 A Meta respondeu 100 para aquele campo, na versão que usamos hoje. Nada do seu lado. O campo aparece como não lido em vez de aparecer com um valor plausível.
aviso_nao_saiu 503 Passou em tudo e o e-mail de aviso ao dono da conta não saiu, então a credencial não saiu junto. Tente de novo em alguns minutos. O aviso não é formalidade: como a Zera não consegue revogar a credencial, ele é a única forma de alguém descobrir uma exibição indevida.
falha_nossa 503 O cofre não abriu, a rede caiu, ou algo do nosso lado não respondeu. Tente de novo em alguns minutos. Se continuar, escreva para a Zera: nem todo caminho que leva a este código gera alerta do nosso lado.
indisponivel 503 A operação não foi concluída, e nada foi gravado pela metade. Tente de novo em alguns minutos.
corpo, json 400 O corpo do pedido veio vazio, grande demais, ou não era JSON válido. Confira o que o seu cliente HTTP está enviando.
interno 500 Falha nossa. Nenhuma resposta que chega ao painel carrega detalhe, e isso é de propósito: a mensagem crua de um erro de banco carrega nome de coluna e às vezes o próprio valor. Escreva para a Zera com o horário. É um dos poucos casos desta tabela em que você não tem o que fazer sozinho.

O erro que não dá erro

Três casos em que nada falha, nada aparece no seu registro, e a mensagem nunca chega.

Nenhum dos três produz um código de erro. É por isso que eles têm seção própria: quem procura o defeito na tabela de códigos passa o dia procurando uma linha que não existe.

1. O webhook apontado no nível da conta, que some na reconexão

Esta seção mudou em 23 de setembro de 2026. Até 22 de setembro ela dizia que o campo de webhook do painel guardava o endereço e não configurava nada na Meta. Isso era verdade e deixou de ser: desde 23 de setembro, com a escrita ligada em produção, o salvamento aponta a sobreposição no seu número. O código que faz isso existe desde 15 de setembro, e até o dia 22 ficou desligado. O caso que sobra é outro, e continua sendo um erro que não dá erro.

A Meta aceita sobreposição de endereço em três níveis: número, conta de WhatsApp e aplicativo. Apontar no nível da conta funciona, a releitura confirma, as mensagens começam a chegar, e um dia elas param.

Por quê: toda vez que o seu número é reconectado, por autorização revogada ou por troca de número, a Zera assina o aplicativo dela na sua conta com POST /{waba_id}/subscribed_apps sem corpo. A própria Meta documenta que assinar sem parâmetros apaga a sobreposição da conta. A sobreposição do número não é tocada por isso, e tem precedência sobre a da conta.

O sintoma: tudo funcionou por semanas, o número foi reconectado num dia qualquer, e a partir dali o seu servidor parou de receber. Nada dá erro, nenhuma configuração sua mudou, e o painel não tem como saber de uma sobreposição que ele não escreveu.

O que fazer: aponte pelo painel, em Canais, no número, aba Detalhes, cartão Ajustes de API. Ele escreve no nível do número e relê para confirmar. O passo a passo, e o caminho manual para quando o painel devolver erro, estão em Receber mensagens. Aquela página também diz, com todas as letras, que nenhum número percorreu isso de ponta a ponta em produção até hoje.

2. O escopo que ninguém confere

A conexão do número confere que o seu token carrega whatsapp_business_management, que é o escopo de gerenciar a conta. Se ele não carregar esse escopo com conta nenhuma, a conexão falha ali mesmo, com mensagem clara.

O escopo que faz POST /{phone_number_id}/messages funcionar é outro: whatsapp_business_messaging. Ele não é conferido em lugar nenhum do produto. A busca por esse nome nas pastas de código, functions/ e public/js/, devolve zero ocorrências: ele só aparece em páginas desta documentação. E é para você poder conferir por conta própria, não para você acreditar na nossa palavra.

Quais escopos a janela de conexão pede é configuração do Embedded Signup, guardada na Meta atrás do identificador de configuração, e ela vive fora deste repositório. Então esta página não afirma que o seu token tem o escopo de envio, nem que ele é pedido: ela afirma a única coisa que dá para conferir aqui, que é que nada verifica.

O sintoma: a conexão termina bem, o painel mostra o número, a leitura de estado na Meta funciona, e o primeiro envio volta erro 200. Como as leituras usam o escopo de gerenciamento, tudo o que você olha na tela parece saudável.

O que fazer: reconecte o número e autorize tudo o que a Meta pedir na janela. Se o 200 voltar no envio depois disso, escreva para a Zera antes de reescrever o seu código: o problema não está nele.

3. Aceita não é entregue

A resposta de um envio bem-sucedido traz "message_status": "accepted" e um wamid. Aceita quer dizer que a Meta recebeu a sua mensagem e assumiu a entrega. Não quer dizer que ela chegou no aparelho de alguém.

Entrega e leitura existem em um lugar só: no corpo do webhook, dentro de statuses[], com o mesmo wamid que voltou no envio. Ou seja, enquanto o seu servidor não estiver recebendo os eventos daquele número, você não tem como saber se alguma mensagem sua chegou.

O que fazer agora: guarde o wamid de todo envio, mesmo antes de o recebimento estar de pé. Sem ele, quando os eventos começarem a chegar, entrega e leitura chegam sem dono e não dá para ligar nenhuma delas ao que você mandou.

Quando nada disso resolve

Escreva para [email protected]. Um chamado com estas cinco coisas costuma ser respondido na primeira resposta; sem elas, a primeira resposta é um pedido de informação.

  • A resposta crua da Graph, o objeto error inteiro, com code, error_subcode, error_data.details, fbtrace_id e message. Copie como veio, sem resumir: o fbtrace_id é o que o suporte da Meta pede.
  • O wamid, quando houve envio. É por ele que nós encontramos a mensagem do lado da Meta.
  • O horário, com o fuso. Sem fuso, três horas de diferença viram três horas de procura no lugar errado.
  • O método e o caminho exatos que você chamou, incluindo a versão. Sem o token, e sem o cabeçalho Authorization.
  • O telefone e o phone_number_id do número envolvido.

Nunca mande o token.

Nós não precisamos dele para investigar, e ele não pode ser trocado depois: a Zera não consegue gerar outro nem revogar o atual. Se você já colou o token em algum lugar, o caminho é a Meta, no Gerenciador de Negócios da sua empresa.