ZAP-API
PreçosCasos de UsoBlogDocsLogin
Começar grátis
  1. Blog
  2. Meta Pixel + CAPI no WhatsApp: rastrear conversão browser e servidor
Tutorial

Meta Pixel + CAPI no WhatsApp: rastrear conversão browser e servidor

Como configurar Meta Pixel e Conversions API para rastrear conversões geradas por integrações WhatsApp, com deduplicação browser+servidor e advanced matching.

28 de maio de 2026·13 min de leitura·Equipe Editorial ZAP API

Você gasta R$3 mil em mídia paga no Facebook/Instagram, leads chegam pelo WhatsApp, fecham venda — e o Meta Ads Manager mostra 0 conversões. O CPL parece R$45 no relatório, quando na verdade é R$11. Esse cenário é diário em e-commerces que rodam tráfego pago e fecham via WhatsApp: sem rastreamento server-side, 40-60% das conversões somem do funil.

O motivo é que o Meta Pixel browser-side só captura o que acontece no site. Quando o usuário sai do site, abre WhatsApp e fecha venda lá, o evento de Purchase nunca volta pro Pixel. A solução é Conversions API (CAPI) — você dispara o evento direto do servidor após o webhook de pagamento, com deduplicação contra o Pixel browser. Resultado: Match Quality acima de 7/10, otimização de campanha real, e CPA cair 30-50% nos primeiros 90 dias.

Pixel browser vs CAPI server-side

Como funciona o Pixel browser

Você cola um snippet JavaScript no <head> do site. Quando o visitante navega, ele dispara fbq("track", "ViewContent"), etc. Limitações:

  • Bloqueado por ad blockers e iOS 14+ ATT
  • Perde eventos quando usuário sai do site (no caso WhatsApp)
  • Match quality cai por falta de dados estruturados

Como funciona o CAPI server-side

Seu backend dispara HTTP POST direto pra Graph API com payload assinado. Vantagens:

  • Não passa pelo browser — imune a ad blocker
  • Captura conversão fora do site (pagamento via webhook PIX/Stripe)
  • Permite Advanced Matching com mais sinais (IP, user-agent, fbc, fbp)

Para máximo resultado: rode os dois em paralelo com mesmo event_id — Meta deduplica em 48h e usa o sinal de mais alta qualidade.

Os 5 eventos do funil WhatsApp

  1. ViewContent: visitante abriu produto/landing page
  2. Lead: clicou em CTA "Falar no WhatsApp" (sai do site)
  3. InitiateCheckout: bot WhatsApp gerou cobrança PIX/cartão
  4. CompleteRegistration: cliente cadastrou (se vende SaaS)
  5. Purchase: webhook de pagamento confirmou — momento de ouro

Pixel browser: snippet base

<!-- No <head> do site -->
<script>
  !function(f,b,e,v,n,t,s){if(f.fbq)return;n=f.fbq=function(){n.callMethod?
  n.callMethod.apply(n,arguments):n.queue.push(arguments)};if(!f._fbq)f._fbq=n;
  n.push=n;n.loaded=!0;n.version='2.0';n.queue=[];t=b.createElement(e);t.async=!0;
  t.src=v;s=b.getElementsByTagName(e)[0];s.parentNode.insertBefore(t,s)}(window,
  document,'script','https://connect.facebook.net/en_US/fbevents.js');
  fbq('init', 'SEU_PIXEL_ID');
  fbq('track', 'PageView');
</script>

Em React/Next:

// components/MetaPixel.tsx
"use client";
import Script from "next/script";

export default function MetaPixel() {
  return (
    <Script id="fb-pixel" strategy="afterInteractive">
      {`
        !function(f,b,e,v,n,t,s){if(f.fbq)return;n=f.fbq=function(){...
        fbq('init', '${process.env.NEXT_PUBLIC_META_PIXEL_ID}');
        fbq('track', 'PageView');
      `}
    </Script>
  );
}

Disparo de Lead quando clica no WhatsApp

// Component LinkWhatsApp.tsx
"use client";
import { useState } from "react";

export default function LinkWhatsApp({ telefoneNumero, mensagem }) {
  const [clicado, setClicado] = useState(false);

  function handleClick() {
    const eventId = `lead_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;

    // Pixel browser
    if (typeof window.fbq === "function") {
      window.fbq("track", "Lead", { content_name: "whatsapp_cta" }, { eventID: eventId });
    }

    // Server-side via fetch (próxima seção mostra o backend)
    fetch("/api/track-lead", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        eventId,
        url: window.location.href,
        userAgent: navigator.userAgent,
        fbc: getCookie("_fbc"),
        fbp: getCookie("_fbp"),
      }),
    });

    setClicado(true);
    window.open(`https://wa.me/${telefoneNumero}?text=${encodeURIComponent(mensagem)}`, "_blank");
  }

  function getCookie(name) {
    const match = document.cookie.match(new RegExp(`(^| )${name}=([^;]+)`));
    return match ? match[2] : null;
  }

  return (
    <button onClick={handleClick} disabled={clicado}>
      Falar no WhatsApp
    </button>
  );
}

Backend: CAPI server-side

import axios from "axios";
import crypto from "crypto";

const META_PIXEL_ID = process.env.META_PIXEL_ID;
const META_CAPI_TOKEN = process.env.META_CAPI_TOKEN;

function hashSHA256(value) {
  return crypto.createHash("sha256").update(value.trim().toLowerCase()).digest("hex");
}

function normalizeBRPhone(phone) {
  // 11 dígitos -> adiciona 55 -> hash
  const digits = phone.replace(/\D/g, "");
  const withCountry = digits.startsWith("55") ? digits : `55${digits}`;
  return hashSHA256(withCountry);
}

export async function fireMetaEvent({ eventName, eventId, userData, customData = {} }) {
  if (!META_PIXEL_ID || !META_CAPI_TOKEN) {
    console.warn("META_PIXEL_ID ou META_CAPI_TOKEN não configurados");
    return false;
  }

  const userDataHashed = {};
  if (userData.email) userDataHashed.em = [hashSHA256(userData.email)];
  if (userData.phone) userDataHashed.ph = [normalizeBRPhone(userData.phone)];
  if (userData.firstName) userDataHashed.fn = [hashSHA256(userData.firstName)];
  if (userData.lastName) userDataHashed.ln = [hashSHA256(userData.lastName)];
  if (userData.externalId) userDataHashed.external_id = [hashSHA256(userData.externalId)];
  if (userData.country) userDataHashed.country = [hashSHA256(userData.country)];

  // Sinais não-hasheados
  if (userData.clientIp) userDataHashed.client_ip_address = userData.clientIp;
  if (userData.clientUserAgent) userDataHashed.client_user_agent = userData.clientUserAgent;
  if (userData.fbc) userDataHashed.fbc = userData.fbc;
  if (userData.fbp) userDataHashed.fbp = userData.fbp;

  const payload = {
    data: [
      {
        event_name: eventName,
        event_time: Math.floor(Date.now() / 1000),
        event_id: eventId,
        action_source: "website",
        event_source_url: customData.eventSourceUrl,
        user_data: userDataHashed,
        custom_data: customData,
      },
    ],
  };

  // Se em modo de teste, adiciona test_event_code
  if (process.env.META_TEST_EVENT_CODE) {
    payload.test_event_code = process.env.META_TEST_EVENT_CODE;
  }

  try {
    const { data } = await axios.post(
      `https://graph.facebook.com/v20.0/${META_PIXEL_ID}/events`,
      payload,
      { params: { access_token: META_CAPI_TOKEN }, timeout: 5000 }
    );
    console.log(`[CAPI] ${eventName} enviado: events_received=${data.events_received}`);
    return true;
  } catch (err) {
    console.error(`[CAPI] erro ${eventName}`, err.response?.data || err.message);
    return false;
  }
}

Disparo de Purchase no webhook de pagamento

// Quando webhook do gateway confirma pagamento
app.post("/webhooks/pagamento", async (req, res) => {
  res.status(200).send("ok"); // ack imediato
  if (req.body.event !== "PAYMENT_COMPLETED") return;

  const { correlationID, value } = req.body.charge;
  const reserva = await db.reserva.findFirst({
    where: { correlationID },
    include: { cliente: true },
  });
  if (!reserva) return;

  // Marca como pago
  await db.reserva.update({
    where: { id: reserva.id },
    data: { status: "pago", pagaEm: new Date() },
  });

  // Dispara Purchase no Meta CAPI
  // event_id deve ser idêntico ao que o pixel browser usaria (se houver)
  const eventId = `purchase_${reserva.id}`;

  await fireMetaEvent({
    eventName: "Purchase",
    eventId,
    userData: {
      email: reserva.cliente.email,
      phone: reserva.cliente.telefone,
      firstName: reserva.cliente.nome.split(" ")[0],
      lastName: reserva.cliente.nome.split(" ").slice(1).join(" "),
      externalId: reserva.cliente.id,
      country: "br",
      fbc: reserva.cliente.fbc, // capturado no momento do click no link
      fbp: reserva.cliente.fbp,
      clientIp: reserva.cliente.lastIp,
      clientUserAgent: reserva.cliente.lastUserAgent,
    },
    customData: {
      currency: "BRL",
      value: value / 100,
      content_ids: [reserva.pacoteId],
      content_type: "product",
      eventSourceUrl: `https://seuapp.com/reserva/${reserva.id}`,
    },
  });
});

Deduplicação browser+server

Se você dispara Purchase pelo browser (cliente em página de obrigado pós-pagamento) E pelo CAPI (webhook), Meta veria 2 conversões. Solução: mesmo event_id nos dois lados — Meta deduplica em 48h.

// Página de obrigado pós-pagamento (browser)
<script>
  fbq("track", "Purchase", {
    currency: "BRL",
    value: 1499,
    content_ids: ["pacote_maragogi_premium"],
  }, {
    eventID: "purchase_reserva_xyz" // mesmo ID que CAPI vai usar
  });
</script>

// Backend
await fireMetaEvent({
  eventName: "Purchase",
  eventId: "purchase_reserva_xyz", // idêntico
  // ...
});

Advanced Matching: subindo Match Quality

Match Quality 0/10 = Meta não consegue casar evento com usuário. Match Quality 8+/10 = otimização robusta. Pra subir:

  • Sempre envie email + phone hasheados (ph e em)
  • Inclua external_id (seu user_id interno) — ajuda muito
  • Capture fbc/fbp no primeiro click e persista no perfil do cliente
  • Inclua client_ip_address + client_user_agent no momento do registro
  • Hash sempre lowercase e trimmed (Meta exige)

Casos práticos

E-commerce médio: CPA caiu 38%

Antes: Meta Ads CPA reportado R$112. Após CAPI + dedup, CPA real revelado R$71. Algoritmo do Meta passou a otimizar pra essas conversões "novas" (que não conseguia ver antes), CPA continuou caindo nos 90 dias seguintes até R$58.

SaaS B2B: CompleteRegistration de 0.5/10 pra 7.8/10

Adicionou external_id (user_id), fbc/fbp do cookie, IP/UA do request — Match Quality saltou. Custo por registro caiu 41% no Looker.

Agência de viagem: Purchase fora do funil web

Cliente clicou no Insta → falou no WhatsApp → fechou via PIX. Antes: Pixel browser não via Purchase, CPA Insta parecia infinito. Após CAPI no webhook PIX: Purchase aparece com event_id único, otimização de público similar começou a converter mais.

FAQ

Como gerar token CAPI?

Events Manager → seu Pixel → Settings → Generate Access Token. Cria System User no Business Manager se necessário. Token não expira (mas pode ser revogado). Guarde em variável de ambiente, NUNCA no front.

Para que serve test_event_code?

Em Events Manager > Test Events, copie o código (TESTxxxxx). Ao incluir no payload, eventos aparecem em real-time na interface — útil pra debugar antes de ativar produção. Remova depois pra eventos contarem na otimização.

Por quanto tempo Meta deduplica?

48 horas após o primeiro evento. Se browser dispara hoje 14h e CAPI dispara amanhã 15h com mesmo event_id, deduplica. Se passar de 48h, contam dois eventos separados (ruim).

Conversion Lift mostra resultado real?

Sim — é o teste estatístico oficial. Meta divide audience em controle e exposto, mede uplift de conversão atribuível a anúncios. Requer volume mínimo (10k+ conversões/mês). Para campanhas menores, fique em CPA + dedup browser/CAPI.

Signal quality afeta otimização?

Diretamente. Quando signal quality < 5, Meta limita o uso desses sinais pra targeting. Quando > 7, sinais entram em Lookalike Audience, Custom Audience e VOC otimization. Diferença de CPA entre 5 e 8 chega a 35%.

Pronto para captar todas as conversões do seu funil WhatsApp? A ZAP API tem endpoint de webhook que dispara automaticamente em pagamentos confirmados — basta plugar no seu CAPI handler e otimização de mídia melhora em 30 dias. Criar conta grátis e veja exemplos no painel.

Experimente a ZAP API gratuitamente

7 dias de trial sem precisar de cartão. A partir de R$29/mês*.

Criar instância grátis
EE
Equipe Editorial ZAP APIRevisão técnica

Desenvolvedores e especialistas em integrações WhatsApp. Todo conteúdo passa por revisão técnica para garantir precisão e aplicabilidade.

Ver perfil completoDocumentaçãoTrial grátis

Leia também

Tutorial · 12 de jun. de 2026 · 12 min

Tipos de mensagem do WhatsApp via API: enquete, localização, contato, evento e mais

Catálogo completo dos tipos de mensagem da API de WhatsApp com o corpo JSON exato e exemplos prontos: texto, mídia, enquete (poll), localização, contato, evento, link e figurinha.

Tutorial · 13 de jun. de 2026 · 10 min

Reagir, editar, apagar, fixar e encaminhar mensagens do WhatsApp via API

Vá além do envio: as ações de mensagem da API de WhatsApp com endpoint exato — reação com emoji, edição, apagar para todos, fixar no topo, encaminhar e marcar como lida.

Tutorial · 14 de jun. de 2026 · 11 min

Autenticação 2FA e OTP por WhatsApp via API: mais barato e confiável que SMS

Como enviar códigos de verificação (OTP/2FA) por WhatsApp via API REST: fluxo completo, código de geração e validação, cuidados de segurança e por que verificar o número antes.

Tutorial · 30 de abr. de 2026 · 13 min

Anti-ban WhatsApp: 12 práticas de engenharia que protegem sua instância

Por que o WhatsApp bane e como evitar: warmup, throttle, spintax, padrões comportamentais e proteções automáticas. Guia técnico de 2026.

Tópicos:Chatbots com IAE-commerceAPI WhatsApp

Explore também

Casos de usoWhatsApp API por segmentoComparativoZAP API vs alternativasPreçosPlanos e o que está inclusoGlossárioTermos técnicos de WhatsApp API
ZAP-API

API REST para WhatsApp com webhooks assinados, Meta Pixel/CAPI e compliance LGPD. Sem aprovação da Meta.

Status operacional🇧🇷 Feito no Brasil

Produto

  • Preços
  • Casos de uso
  • Comparativo
  • Trial grátis
  • Dashboard

Recursos

  • Documentação
  • Blog
  • Glossário
  • RSS Feed

Empresa

  • Sobre
  • Imprensa
  • Termos de uso
  • Privacidade
  • Criar conta
  • Login

Contato

  • [email protected]
  • [email protected]
  • Resposta em até 24h úteis
© 2026 ZAP-API — Todos os direitos reservados·CNPJ 42.130.949/0001-56·Termos·Privacidade

Desenvolvido por PreviusIA