O pior cenário com WhatsApp em produção é descobrir que a instância caiu pelo cliente reclamando, não pelo monitoramento. Isso acontece toda semana com quem só verifica conexão por status binário (conectado/desconectado) — porque entre os dois extremos existe uma zona cinza de degradação onde a instância "está conectada" mas mensagens falham silenciosamente.
Para resolver isso, a ZAP API expõe um Session Quality Score de 0 a 100 que combina vários sinais em um único número. Este post mostra como usar esse score para detectar problemas antes que afetem clientes.
Por que status binário não basta
Casos reais que acontecem em produção:
- Instância "conectada" mas com 30% de taxa de erro nas últimas 100 mensagens (o algoritmo do WhatsApp começou a rate-limitar)
- Instância "conectada" mas com latência de 8 segundos por envio (gateway sob pressão)
- Instância que conectou e desconectou 12 vezes nas últimas 6 horas (problema de NAT ou bateria do celular)
- Instância nova "conectada" mas sem nenhuma mensagem trafegada nos últimos 7 dias (handshake incompleto)
Em todos esses casos, status binário diz CONECTADA. Score diz "70", "55", "30" — números que disparam alerta.
O que o score mede
Quatro componentes, somados com pesos:
- Conexão (40%): está conectada agora? Há quanto tempo sem cair?
- Latência (20%): tempo médio de envio nas últimas 100 mensagens (alvo <2s).
- Taxa de erro (20%): percentual de mensagens com falha nas últimas 24h (alvo <2%).
- Estabilidade (20%): quantas desconexões ocorreram nas últimas 24h (alvo 0-2).
Endpoint
GET https://api.zap-api.tech/v1/instances/:id/health
Authorization: Bearer tk_seu_token
Response exemplo (instância saudável)
{
"instanceId": "inst_abc123",
"score": 92,
"state": "CONNECTED",
"components": {
"connection": { "value": 100, "weight": 40 },
"latencyMs": { "avg": 850, "p95": 1400, "value": 95, "weight": 20 },
"errorRate": { "rate": 0.012, "value": 88, "weight": 20 },
"stability": { "disconnections24h": 1, "value": 90, "weight": 20 }
},
"lastDisconnectAt": "2026-05-08T09:14:22Z",
"lastInboundAt": "2026-05-08T11:42:08Z",
"lastOutboundAt": "2026-05-08T11:43:15Z"
}
Response exemplo (instância degradada)
{
"instanceId": "inst_xyz789",
"score": 48,
"state": "DEGRADED",
"components": {
"connection": { "value": 100, "weight": 40 },
"latencyMs": { "avg": 6200, "p95": 9100, "value": 30, "weight": 20 },
"errorRate": { "rate": 0.18, "value": 22, "weight": 20 },
"stability": { "disconnections24h": 8, "value": 50, "weight": 20 }
}
}
Score 48 = atenção imediata. Latência alta + taxa erro alta + 8 desconexões = sintomas de rate limit do WhatsApp ou problema de rede.
Máquina de estados
O campo state é categórico:
- CONNECTED: score >= 80, tudo nominal.
- DEGRADED: score 40-79, conectada mas com sinais ruins. Hora de investigar.
- DISCONNECTED: não conectada. Score 0.
A transição CONNECTED → DEGRADED é o sinal valioso — você ganha minutos ou horas para reagir antes de virar DISCONNECTED.
Polling com alerta automático
// monitor.js
import axios from "axios";
const ZAP = axios.create({
baseURL: "https://api.zap-api.tech/v1",
headers: { Authorization: `Bearer ${process.env.ZAP_TOKEN}` },
});
const SLACK_WEBHOOK = process.env.SLACK_WEBHOOK;
const INSTANCIAS = ["inst_abc123", "inst_def456", "inst_ghi789"];
const ESTADO = new Map(); // memoiza último state por instância
async function checar() {
for (const id of INSTANCIAS) {
try {
const { data } = await ZAP.get(`/instances/${id}/health`);
const anterior = ESTADO.get(id);
ESTADO.set(id, data.state);
// Score caiu abaixo de 70: alerta
if (data.score < 70 && anterior !== data.state) {
await alertarSlack({
texto: `⚠️ Instância ${id} degradada (score ${data.score})`,
detalhes: data.components,
});
}
// Voltou ao normal: aviso de recuperação
if (data.state === "CONNECTED" && anterior === "DEGRADED") {
await alertarSlack({
texto: `✅ Instância ${id} recuperada (score ${data.score})`,
});
}
} catch (err) {
console.error("Erro lendo health de", id, err.message);
}
}
}
async function alertarSlack({ texto, detalhes }) {
await axios.post(SLACK_WEBHOOK, {
text: texto,
blocks: detalhes
? [
{ type: "section", text: { type: "mrkdwn", text: texto } },
{ type: "section", text: { type: "mrkdwn", text: `\`\`\`${JSON.stringify(detalhes, null, 2)}\`\`\`` } },
]
: undefined,
});
}
// Roda a cada 5 minutos
setInterval(checar, 5 * 60 * 1000);
checar();
Integração com email (Resend)
import { Resend } from "resend";
const resend = new Resend(process.env.RESEND_KEY);
async function alertarEmail({ instanceId, score, componentes }) {
await resend.emails.send({
from: "[email protected]",
to: "[email protected]",
subject: `[ZAP] Instância ${instanceId} com score ${score}`,
html: `Detalhes:
${JSON.stringify(componentes, null, 2)}`,
});
}
Integração com PagerDuty (escalonamento)
Para times de plantão 24/7, dispare incidente PagerDuty quando score cair abaixo de 30 ou ficar DEGRADED por mais de 30 minutos:
async function pagerDuty({ titulo, descricao, severidade }) {
await axios.post("https://events.pagerduty.com/v2/enqueue", {
routing_key: process.env.PD_ROUTING_KEY,
event_action: "trigger",
payload: {
summary: titulo,
severity: severidade,
source: "zap-monitor",
custom_details: descricao,
},
});
}
O que fazer quando score cai
- Score 70-79: investigue mas não acorde ninguém. Veja qual componente está ruim.
- Score 40-69: ação no horário comercial. Reconecte a instância via painel ou via
POST /v1/instances/:id/reconnect. - Score <40: ação imediata. Provavelmente o WhatsApp rate-limitou — reduza throughput, espere 1-2 horas e teste.
- Score 0 + state DISCONNECTED: reconectar (gerar QR ou pairing code). Se for cair de novo em <1h, problema é estrutural.
Casos práticos
Caso 1: Detecção de rate limit silencioso
Loja de e-commerce dispara 800 confirmações de pedido em 30 minutos durante Black Friday. Score caiu de 95 para 48 em 20 minutos. Componente errorRate subiu para 0.22. Ops reduziu throughput para 200/30min via fila e score voltou para 80 em 1h. Sem o monitoramento, teriam perdido pedidos.
Caso 2: Bateria do celular linkado morrendo
Cliente operava com celular Android antigo plugado direto na tomada, sem otimização de bateria. Sistema operacional matava o WhatsApp em background a cada 2-3 horas. Score oscilando 60-90 com 4-6 desconexões/dia. Diagnóstico: trocar para celular dedicado. Score subiu para 95+ em 24h.
Caso 3: NAT com timeout curto
Empresa em rede corporativa com firewall agressivo cortando conexões longas. Instância caía a cada 30 minutos. Score nunca passava de 65. Diagnóstico: configurar keep-alive no firewall ou mudar a instância para rede separada.
FAQ
O que fazer se o score ficar < 50 sem motivo aparente?
99% das vezes é rate limit silencioso do WhatsApp porque você passou de algum threshold de envio. Reduza throughput pela metade nas próximas 6 horas e revise warmup. O score volta sozinho conforme o algoritmo reabaixa o flag.
Qual diferença entre score 0 e instância DISCONNECTED?
São equivalentes — quando state=DISCONNECTED, o score é forçado para 0 (não faz sentido medir latência de instância desconectada). A diferença prática é que score <40 com state=CONNECTED indica problema iminente; score=0 indica problema atual.
Qual frequência ideal de polling?
5 minutos para produção normal, 1 minuto para operações críticas (financeiro, agendamentos), 15 minutos para baixo volume. Polling abaixo de 30s é desperdício — score só recalcula quando há nova mensagem ou desconexão.
Já vi falso positivo. Como evitar acordar todo mundo de madrugada?
Aplique janela de confirmação: só dispara alerta se score < 70 por 3 leituras seguidas (15 minutos com polling de 5min). Reduz drasticamente falso positivo de leituras isoladas.
Como o score se comporta em sandbox?
Sandbox tem score sintético — sempre 100 ou 0, sem componentes intermediários. Score realista só em instâncias produtivas com mensagens reais sendo trafegadas.
Próximo passo
Toda conta tem health endpoint disponível desde a primeira instância. Criar conta grátis e configure seu monitor em 10 minutos.