SuperdindaDocs

Saúde e heartbeat

Como saber se o lado WhatsApp está de pé — endpoints de saúde, sinais de vida, o que o backoffice mostra e onde procurar nos logs.

Esta página trata da saúde da infraestrutura (processos, filas, sessões). O backoffice segue a regra de AD-016: mostra estado, nunca conteúdo. Nada aqui expõe texto de mensagem, nome ou foto de contato; a identidade da pessoa protegida aparece mascarada.

Endpoints de saúde

ProcessoRotaO que checaResposta
apiGET /healthPostgres e RedisJSON sem envelope, status: ok (200) ou degraded (503), uptime e ms por serviço
gatewayGET /health (porta 3002)Redisok (200) ou degraded (503)
WAHAGET /health com X-Api-Keyo próprio WAHAusado pelo healthcheck do compose (dev a cada 10 s, prod a cada 15 s)
workers——sem rota HTTP; no boot aborta se Postgres ou Redis não respondem
Checagem rápida (local)
curl -s http://localhost:3000/health | jq
curl -s http://localhost:3002/health | jq
curl -fsS -H "X-Api-Key: <WAHA_API_KEY>" http://localhost:3001/health
docker compose ps waha

Em produção o WAHA e o gateway ficam só na rede interna do compose (expose), sem porta pública; o /health da api é o que o orquestrador lê.

Em aberto

O /health da api não inclui o WAHA, e os workers não têm endpoint de saúde. Um worker travado ou um WAHA fora do ar não aparecem em nenhum /health do backend.

Sinais de vida que existem

SinalOndeFrequência
ping no SSE da contaGET /accounts/{uid}/eventsa cada 5 s
ping no SSE público do pareamentoGET /consent-requests/{token}/eventsa cada 5 s
channel_syncs.heartbeat_atimportação de históricoa cada passo da importação
pairings.last_seen_atpareamentosó quando a sessão conecta ou reconecta (paired)
session.status do WAHAwebhook → gateway → workersquando o WAHA muda o estado da sessão

Os pings só mantêm a conexão SSE viva. O estado de cada sessão depende dos webhooks: o nosso lado não pergunta ao WAHA de tempos em tempos.

Em aberto

Não há heartbeat por sessão nem reconciliação periódica com o WAHA: ChannelAdapter.state() existe, mas nada o chama. Se um webhook se perder, o pareamento fica com o último estado conhecido. last_seen_at não avança enquanto a sessão segue conectada.

Estado de uma sessão

  • No nosso banco: pairings.status (ver Pareamento), failure_reason, reconnect_attempts, next_retry_at, provider_worker.

  • No WAHA: a sessão se chama sd-<pairingUid>.

    curl -fsS -H "X-Api-Key: <WAHA_API_KEY>" \
      http://localhost:3001/api/sessions/sd-<pairingUid>

    Localmente também dá para usar o dashboard em http://localhost:3001/dashboard.

O que o backoffice mostra

TelaRota do backendMostra
Visão geralGET /admin/overviewcontagens agora: pairingsPaired, pairingsDegraded, proxiesActive, proxiesBurned, syncsRunning, syncsWaiting, syncsStuck, fila
Clientes → ConexõesGET /admin/connections?status=&channel=por canal de cada vaga: status do pareamento, pairedAt, lastSeenAt, worker do WAHA, rótulo do proxy, status e etapa da sincronização
Detalhe da conta → linha do tempo da vagaGET /admin/seats/{uid}/timelineeventos de consentimento, pareamento (provider.failed, reconnect, cancelled…), sincronização e assinatura, só o tipo
Infraestrutura → ProxiesGET /admin/proxies, …/{uid}/bindingsocupação por endereço, status, quem está em cada um
Infraestrutura → Compras / Orçamento/admin/proxy-supply/*saldo, renovações, validade, tetos

Operação → Sessões e Infraestrutura → Dispositivos aparecem no menu, desligadas: esperam rotas que ainda não existem.

Scripts: o que cada um checa

ScriptChecaNão checa
bun run wa:doctora conta WhatsApp Business da Meta (número, templates, assinatura do webhook) usada para mandar o link de consentimento; -- <número> envia o template de verdadesessões WAHA
bun run verify:probe -- audio <arquivo> "<frase>"o verificador de evidência (OpenRouter) com arquivos locaisnada do WhatsApp
bun run pair:qr -- <seat-uid>grava o QR (ou mostra o código) do pareamento aberto; só dev, lê o banco diretosaúde

Em aberto

Nenhum script verifica as sessões WAHA (estado, proxy, webhook). Também não há alerta para operadores: hoje quem é avisado de uma queda é o responsável, pelas notificações pairing.disconnected, pairing.identity_mismatch e pairing.reconnect_required.

Logs

Os processos usam pino: legível em dev, JSON em produção (LOG_FORMAT: json).

Local
docker compose logs -f waha
bun run gateway:dev   # e bun run workers:dev, cada um no seu terminal
MensagemProcessoSignifica
waha webhook refused: missing or bad signaturegatewayHMAC ausente ou errado (chave divergente entre WAHA e gateway)
WAHA_WEBHOOK_HMAC_KEY is required…gatewaynão subiu sem a chave
provider stream read failedworkersleitura do Redis falhou
provider event failed; left pendingworkershandler lançou; a entrada ficou pendente
reconnect attempts issued / reconnect restart failedworkerstick de reconexão
challenge refresh failed / first challenge fetch failedworkersWAHA não entregou QR/código
pairing identity mismatchworkersnúmero conectado não é o da pessoa
no purge configured: the provider keeps its plaintext copyworkersa cópia do histórico no waha-db não foi apagada
Fila do provedor no Redis
redis-cli XLEN provider:waha
redis-cli XINFO GROUPS provider:waha
redis-cli XPENDING provider:waha workers

Fontes

  • backend/src/http/routes/system/health.route.ts, backend/apps/gateway/main.ts, backend/apps/workers/{main,consume-provider-events,reconnect-ticker,challenge-refresher}.ts
  • backend/src/http/routes/events/stream.route.ts, backend/src/http/routes/consents/pairing.route.ts
  • backend/src/domains/insights/use-cases/{overview,operator-lists,seat-timeline}.ts, backend/src/http/routes/admin/operator-lists.route.ts
  • backend/src/domains/ingest/use-cases/run-sync.ts, backend/src/domains/ingest/channels/whatsapp/waha-ingest.ts
  • backend/scripts/{wa-doctor,verify-probe,pair-qr}.ts, backend/docker-compose.yml, backend/docker-compose.prod.yml
  • apps/backoffice/src/components/area/areas.ts, apps/backoffice/src/components/clients/list-screens.tsx
  • Vault: AD-016 (estado, nunca conteúdo), AD-010

On this page