Solução de problemas
Quebras comuns e como destravá-las.
"O QR nunca aparece"
Sintomas. GET /v1/channels/:id/qr retorna 200 com corpo vazio, ou o painel de QR do dashboard fica em branco.
Causas (em ordem de probabilidade):
- O canal já está em
qr_requirede a string do QR expirou. As strings de QR rotacionam a cada ~30s. O gerenciador de sessão gera uma nova e re-emite um eventoqr.updated. Espere uma rotação. - O WhatsApp pode aplicar rate limit a uma sessão. Recriar sessões repetidamente para o mesmo número dispara limites temporários. Espere 5–10 minutos.
MAX_SESSIONS_PER_WORKERexcedido. O backend rejeita a criação de novos sockets além desse teto (padrão 50). Cheque o/readyz— ele mostra a contagem.
"sessionInvalidated depois de um pareamento bem-sucedido"
O WhatsApp explicitamente nos disse que a sessão está morta. Razões mais comuns:
- O usuário abriu o WhatsApp Web num navegador e essa sessão derrubou a nossa (só uma sessão Web por número).
- O usuário desvinculou o aparelho manualmente em Aparelhos conectados no celular.
- O número foi logado a partir de uma instalação nova do WhatsApp em outro celular.
O que fazer. O auth-state é apagado automaticamente; o canal vai para disconnected. Faça uma nova busca de QR. Se você vê isso repetidamente para o mesmo número, é quase sempre uma situação de "duas sessões Web" — garanta que ninguém está abrindo web.whatsapp.com com o mesmo número.
"429 rate_limited"
O token bucket por cliente está vazio. Padrões: burst de 60, 1 req/seg sustentado.
O que fazer.
- Respeite o header
Retry-After. Ele te diz quando o bucket vai ter um token. - Se você está batendo nisso por uma carga sustentada (não um burst), fale com a gente — a gente aumenta o seu limite.
- Não fragmente entre várias chaves de API para escapar do limite; o bucket é por cliente, não por chave.
"O webhook nunca chega"
Passe por este checklist:
- A URL do endpoint é de DNS público?
localhoste RFC1918 são rejeitados na criação. (Use ngrok para dev local.) - Seu endpoint está retornando 2xx? Cheque
GET /v1/webhook-deliveries?status=failed—last_errorelast_response_statuste dizem o que a gente viu. - Você pausou a fila? Se o seu endpoint ficou fora do ar, o worker pode ter passado na sua frente e queimado o orçamento de retentativas. Entregas que falharam além da tentativa 6 não são re-tentadas automaticamente.
- Você está filtrando no servidor? Se você definiu
events: ['message.received']no endpoint, não vai receber eventoschannel.connected— por design.
"A assinatura do webhook não verifica"
Principais culpados:
- Você está verificando contra um corpo parseado/reserializado. A assinatura é sobre os bytes brutos. No Express, capture-os com
verify: (req, res, buf) => { req.rawBody = buf }no parser JSON. - Deriva de relógio. O servidor impõe uma tolerância de timestamp de 5 minutos. Se o relógio do host do seu endpoint estiver mais que isso do tempo real, todo webhook falha. Rode NTP.
- Segredo errado. Cada endpoint tem seu próprio segredo de assinatura. Se você tem vários endpoints, garanta que está verificando com o que casa com
X-WhatIsUp-Endpoint-Id.
"O dashboard diz que meu canal está connected mas as mensagens não saem"
Confira o óbvio antes de ir mais fundo:
- Você está usando o
channel_idcerto emPOST /v1/channels/:id/messages? É fácil copiar o errado. - O destinatário
toé um MSISDN com código do país, sem+, sem espaços? - Olhe o log de auditoria (aba Atividade no dashboard). Todo envio deixa uma linha.
Se os três estão certos, o problema é a jusante da API:
- O envio foi enfileirado mas o socket do WhatsApp caiu antes de a mensagem sair. Cheque o
/readyz— se o probe de saúde da conexão com o WhatsApp está falhando, a sessão está em mau estado mesmo que a linha digaconnected. - O envio bateu num rate limit do lado do WhatsApp (separado do nosso limitador). Esses aparecem como webhooks
message.failedcomerror.code: rate_limited.
"Reiniciar o backend perdeu minhas sessões"
O auth-state deveria sobreviver a reinícios. Se não sobreviveu:
- Filesystem efêmero. Containers sem um volume persistente em
data/sessions/perdem o auth-state a cada redeploy. Monte um volume. - Permissão incompatível. Se o volume pertence a um uid diferente do processo Node, o gateway falha silenciosamente ao ler o estado existente e cai de volta para "pareamento novo". Cheque as permissões do arquivo.
Onde conseguir ajuda
- O log de auditoria (
audit_eventsno dashboard) registra toda ação que muda estado. - O
/readyzfaz verificações profundas: transação no DB, ping no Redis, probe da conexão com o WhatsApp. - Abra uma issue em github.com/aneps/zappi — inclua o
correlation_iddo header da resposta se você tiver um.