Playbook pra instrumentar qualquer landing page de venda (fora do nosso domínio) com tracking limpo: lead no CRM, eventos no pixel/GA4 e venda confirmada server-side, sem duplicar nada.
← voltar pros guiasPara quem é: quem monta tracking de lançamento (tech/tráfego). O que você consegue ao final: um funil onde a pessoa deixa o contato, vira lead no CRM, e quando compra o sistema marca a venda no CRM e manda o Purchase pro Meta — tudo automático e sem evento duplicado.
O fluxo tem três camadas, cada uma disparando os eventos certos:
LP de venda ──(popup)──► CRM (lead) ──► checkout ──(webhook)──► CRM (comprou) + Meta (Purchase)
PageView Lead InitiateCheckout Purchase
ViewContent (server-side)
| Evento | Dispara quando | Onde |
|---|---|---|
PageView | Carregou qualquer página | Pixel base no <head> |
ViewContent | Carregou a LP de venda | No load da LP |
Lead | Submit do popup (lead real, antes do checkout) | Dentro do JS do popup |
InitiateCheckout | Segue do popup pro checkout | Antes do redirect |
Purchase | Compra confirmada | Server-side (CAPI), via webhook do gateway — fonte única |
Se a LP é HTML estático (ou está num domínio que não é o nosso), ela não herda o GTM/pixel do tema. Instala manual:
<head>: pixel base (fbq('init', PIXEL_ID) + PageView) e o GA4 (a property certa).fbq('track','ViewContent').utm_* da URL no load, anexar ao payload do lead e à URL do checkout (CHECKOUT + '?' + utmQuery), senão a atribuição venda→criativo não fecha.No submit do popup, antes de mandar pro CRM:
if (typeof fbq === 'function') fbq('track','Lead'); // lead real
await fetch(CRM_ENDPOINT, { method:'POST', keepalive:true, ... });
if (typeof fbq === 'function') fbq('track','InitiateCheckout'); // vai pro checkout
window.location.href = qs ? CHECKOUT + '?' + qs : CHECKOUT;
keepalive:true garante que o POST completa mesmo com o redirect logo em seguida.
O popup posta nome/e-mail/WhatsApp + UTM pra um handler serverless (função na Vercel) com o token do CRM escondido no servidor. O handler faz, no GHL:
firstName+lastName, não só name.POST /contacts/{id}/tags) — o contacts/upsert SUBSTITUI o conjunto de tags, então tag no upsert apaga as outras.O gateway (Eduzz, Hotmart, etc.) chama um segundo handler no webhook da venda. Quando a venda é aprovada e é do produto certo, ele:
Purchase via Meta Conversions API (server-side), com event_id = pur_<gateway>_<cliente>_{transaction_id} — esse id é o que deduplica: reenvio do webhook não vira Purchase duplicado.comprou (gatilho dos workflows do CRM).Escopo de segurança: o handler só age se a venda for do produto certo (id configurado) ou o contato já estiver no funil — pra não criar oportunidade pra compra de outro produto do mesmo gateway.
Payload mínimo do Purchase pro Meta CAPI:
POST https://graph.facebook.com/v21.0/<PIXEL_ID>/events?access_token=<TOKEN>
{
"data":[{
"event_name":"Purchase",
"event_time": <unix>,
"event_id":"pur_eduzz_comport_<transaction_id>", // dedup
"action_source":"website",
"user_data":{ "em":[sha256(email)], "ph":[sha256(fone)] }, // hash SHA-256
"custom_data":{ "value": <valor>, "currency":"BRL" }
}]
}
Na conta produtora do Eduzz, em console.eduzz.com/webhook/configs (menu WEBHOOK → Configurações), criar uma config nova:
?token=<segredo> de guarda (o handler rejeita 401 sem o token certo).invoice_paid, invoice_refunded, invoice_canceled./webhook/secrets) é só o secret de assinatura HMAC — não é onde se cria o webhook.O payload do Eduzz v3 chega FLAT — o objeto da fatura direto na raiz ({ id, status, buyer:{email,name,cellphone}, items:[{productId}], price:{value} }), sem o envelope { event, data } que a documentação mostra. Se o handler detectar v3 só pela presença de event, ele ignora a venda. Detecte pela presença de buyer/items e desembrulhe data se vier.
O módulo de apps/dev-token (pra registrar via API) pode estar bloqueado pra conta — a UI de Configurações resolve igual e manda o mesmo formato v3.
Handlers na Vercel (projeto dashboard). Deploy: npx vercel --prod. Variáveis de ambiente típicas:
| Var | Pra quê |
|---|---|
<GATEWAY>_POSTBACK_TOKEN | guarda do ?token= |
<GATEWAY>_PRODUCT_ID | reconhece compra direta do produto certo |
| token do Meta | CAPI (system user com permissão no pixel) |
| token do CRM (PIT) | scoped à location certa |
Variável marcada como sensitive no Vercel não volta no vercel env pull (vem vazia). Guarde o valor num lugar seguro fora do Vercel (ex: ~/.config/sirius/). E atenção: se rotacionar o token, atualize a URL no webhook do gateway — senão o webhook real começa a tomar 401 mesmo tendo passado no teste antes.
comprou, e no Meta Events Manager que chegou 1 Purchase (evento de servidor/CAPI, com alguns minutos de atraso).event_id.contacts/upsert substitui tags → use o endpoint aditivo de tags.opportunities/search: não aceita startAfter/startBefore (422), limit máx 100, filtre por pipeline_id server-side (não client-side sobre a página), e a busca tem lag de indexação (não confunda com bug).env pull; rotação quebra webhook configurado.ViewContent no load; UTM lida e repassada.Lead + POST pro handler → upsert + tag aditiva + oportunidade no CRM; depois InitiateCheckout e redirect.Purchase CAPI (dedup por event_id) + tag comprou + avança oportunidade.?token=; payload vem flat.