Métricas de campanha no WhatsApp: entregue, lido e respondido pela API
Enviado não é entregue e entregue não é lido. Como capturar cada status pelo webhook, montar o funil real da campanha, medir a métrica que de fato importa — resposta — e reconhecer no gráfico quando o número está queimando.
"A campanha foi bem?" quase nunca tem resposta, porque quase ninguém captura os eventos que respondem. O envio retorna 200, alguém conclui que deu certo, e o que aconteceu depois se perde.
Este artigo é sobre medir. A fila entrega as mensagens; a medição diz se valeu.
O funil real
| Etapa | O que significa | Onde aparece |
|---|---|---|
| Enfileirado | Você decidiu mandar | Seu banco |
| Enviado | A plataforma aceitou | Resposta da chamada |
| Entregue | Chegou ao aparelho | Webhook delivered |
| Lido | A pessoa abriu | Webhook read |
| Respondido | A pessoa escreveu de volta | Webhook messages |
| Saiu | Pediu descadastro | Seu tratamento de opt-out |
Cada degrau perde gente. Onde perde diz o que consertar — e sem os degraus intermediários você só tem o primeiro e o último.
Capturar os status
Todo evento de status chega em value.statuses, e não em value.messages:
async function processar(evento) {
const value = evento?.entry?.[0]?.changes?.[0]?.value;
for (const s of value?.statuses ?? []) {
await registrarStatus({
messageId: s.id,
status: s.status, // sent | delivered | read | failed
em: new Date(Number(s.timestamp) * 1000),
erro: s.errors?.[0]?.code,
});
}
const msg = value?.messages?.[0];
if (msg) await registrarResposta(msg);
}
Confundir os dois campos é o erro número um de quem escreve o parser pela primeira vez — está entre as 7 causas de webhook que não funciona.
Amarrar status ao envio
Só funciona se você guardou o messageId no momento do envio:
const r = await enviarTemplate(to, template, params);
await db.query(
`UPDATE envios SET status = 'enviado', message_id = $1, enviado_em = NOW()
WHERE campanha_id = $2 AND numero = $3`,
[r.messageId, campanhaId, to],
);
E, ao receber o status:
await db.query(
`UPDATE envios
SET status = $1, ${coluna(status)} = $2
WHERE message_id = $3`,
[status, em, messageId],
);
Uma coluna de timestamp por etapa (entregue_em, lido_em, respondido_em) rende mais que uma coluna de status só: além do funil, você ganha o tempo entre etapas, e é ele que revela problema de ritmo.
O relatório
SELECT
COUNT(*) AS enfileirados,
COUNT(*) FILTER (WHERE status <> 'pendente') AS enviados,
COUNT(*) FILTER (WHERE entregue_em IS NOT NULL) AS entregues,
COUNT(*) FILTER (WHERE lido_em IS NOT NULL) AS lidos,
COUNT(*) FILTER (WHERE respondido_em IS NOT NULL) AS respondidos,
COUNT(*) FILTER (WHERE status = 'falha_definitiva') AS falhas,
ROUND(100.0 * COUNT(*) FILTER (WHERE entregue_em IS NOT NULL)
/ NULLIF(COUNT(*) FILTER (WHERE status <> 'pendente'), 0), 1) AS taxa_entrega,
ROUND(100.0 * COUNT(*) FILTER (WHERE respondido_em IS NOT NULL)
/ NULLIF(COUNT(*) FILTER (WHERE entregue_em IS NOT NULL), 0), 1) AS taxa_resposta
FROM envios
WHERE campanha_id = $1;
A API também expõe números agregados de atendimento, úteis para uma visão geral sem montar tabela — pelo endpoint de estatísticas ou, em conversa, pela ferramenta get_analytics do MCP. Para análise por campanha, porém, o registro no seu banco é o que permite cruzar com pedido, cliente e receita.
Como ler cada número
Taxa de entrega
| Faixa | Leitura |
|---|---|
| > 95% | Base saudável |
| 90–95% | Número desatualizado; hora de higienizar |
| < 90% | Higienização antes da próxima campanha |
Na API oficial isso não é só desperdício: falha repetida prejudica a reputação do número.
Taxa de leitura. Sempre subestima — só aparece para quem mantém a confirmação de leitura ativa. Serve para comparar campanhas entre si, não como número absoluto.
Taxa de resposta. A que importa. É a única que exige ação deliberada, e no WhatsApp ela ainda abre a janela de 24 horas — ou seja, o atendimento seguinte não gera cobrança nova. Uma campanha com boa resposta é mais barata por conversão do que os números brutos sugerem. Vale ler junto com a janela de 24 horas e os templates.
Leitura alta com resposta zero quase sempre significa mensagem que não pede nada. Template com botão de resposta rápida costuma resolver.
Os dois números que avisam antes do problema
Taxa de entrega e leitura contam o passado. Bloqueio e denúncia contam o futuro.
SELECT
c.id, c.nome, c.enviada_em,
ROUND(100.0 * COUNT(*) FILTER (WHERE e.entregue_em IS NOT NULL)
/ NULLIF(COUNT(*), 0), 1) AS entrega,
COUNT(*) FILTER (WHERE e.erro = 'contato_bloqueou') AS bloqueios,
COUNT(*) FILTER (WHERE e.status = 'optout') AS saidas
FROM campanhas c
JOIN envios e ON e.campanha_id = c.id
GROUP BY c.id
ORDER BY c.enviada_em DESC
LIMIT 10;
Olhe a coluna entrega de cima para baixo. Se ela cai campanha após campanha com a mesma base, a reputação está se deteriorando — e o próximo passo é restrição, não aviso.
Quando esse padrão aparece, a resposta é contraintuitiva: mandar menos. Reduzir frequência, revisar o conteúdo, reconfirmar os contatos antigos. Aumentar volume para compensar queda de entrega é o que transforma um problema recuperável em número perdido.
Comparar de verdade
Para uma comparação valer, mude uma variável por vez:
- mesmo público, textos diferentes → o texto;
- mesmo texto, horários diferentes → o horário;
- mesmo texto, com e sem botão → o botão.
Mudar texto, horário e público juntos e concluir que "a nova versão foi melhor" não informa nada sobre o que repetir.
Conclusão
Medir campanha de WhatsApp é guardar o messageId, capturar value.statuses e ter uma coluna de timestamp por etapa. Meia hora de trabalho, uma vez.
O retorno é parar de decidir por impressão: você passa a saber se o problema é a lista, o texto, o horário ou o número — e cada um desses tem uma correção diferente.
Pronto para automatizar seu WhatsApp?
Crie sua conta gratuita e comece a enviar mensagens pela API em minutos.
Começar grátisPerguntas frequentes
Como saber se a mensagem foi entregue pela API do WhatsApp?+
Pelo webhook de status. Depois do envio, chegam eventos em entry[0].changes[0].value.statuses com o id da mensagem e o estado: sent, delivered, read ou failed. Você amarra esses eventos ao envio original pelo messageId que a chamada de envio retornou.
Qual a diferença entre enviado, entregue e lido?+
Enviado significa que a plataforma aceitou a mensagem. Entregue significa que ela chegou ao aparelho. Lido significa que a pessoa abriu a conversa — e só aparece se ela mantiver a confirmação de leitura ativada, então a taxa de leitura sempre subestima a realidade.
Qual taxa de entrega é considerada boa?+
Acima de 95% é saudável. Entre 90% e 95% indica base com número desatualizado. Abaixo de 90% é sinal de lista precisando de higienização — e, na API oficial, tentativa falha repetida também prejudica a reputação do número.
Como medir se a campanha deu resultado?+
Pela taxa de resposta, não pela de leitura. Resposta é a única métrica que exige uma ação deliberada da pessoa, e no WhatsApp ela ainda abre a janela de 24 horas — o que reduz o custo do atendimento seguinte. Campanha com boa leitura e resposta zero costuma indicar mensagem que não pede nada.
Como perceber que o número está queimando?+
Acompanhe bloqueios e denúncias por campanha e a taxa de entrega ao longo do tempo. Entrega caindo campanha após campanha, com a mesma base, é o sinal mais confiável de que a reputação está se deteriorando. Quando isso aparece, reduza volume e frequência antes de perder o número.
Continue lendo
Como criar um chatbot de IA com a API da OpenAI para responder no WhatsApp
Um webhook, uma chamada à API da OpenAI e uma resposta pela WAME API: o código completo de um chatbot de IA que atende no WhatsApp, Instagram e Messenger. Com memória por contato, controle de custo e o que fazer quando a IA não deve responder.
Cobrança por Pix dentro do WhatsApp pela API: como enviar e o que muda na conversão
Mandar o código Pix no WhatsApp resolve o pior ponto da cobrança digital: o cliente não precisa sair do app. Como enviar a cobrança pela API, tratar a confirmação e evitar os erros que transformam a facilidade em suporte.
Erros da API do WhatsApp: o que cada um significa e como tratar
A mensagem não saiu e o log diz apenas 'erro ao enviar'. Os erros que você vai encontrar de verdade — janela fechada, número inválido, template não aprovado, limite atingido, instância caída — e o tratamento certo para cada um.