Raphael Serafim· Publicado em 10 de setembro de 2026· 9 min de leitura

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.

Ver como Markdown

"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

EtapaO que significaOnde aparece
EnfileiradoVocê decidiu mandarSeu banco
EnviadoA plataforma aceitouResposta da chamada
EntregueChegou ao aparelhoWebhook delivered
LidoA pessoa abriuWebhook read
RespondidoA pessoa escreveu de voltaWebhook messages
SaiuPediu descadastroSeu 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

FaixaLeitura
> 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átis

Perguntas 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