Como Rastrear Conversões em SaaS (Vibe Coding com IA)
Guia para implementar tracking de funil SaaS — cadastro, ativação, trial-to-paid, MRR e churn — colável direto em IA assistente
Guia completo para implementar rastreamento de funil em produtos SaaS usando o Track Combo — cadastro grátis, ativação, trial-to-paid, MRR, upgrade e churn. O artigo foi escrito para ser colado direto em uma IA assistente (Cursor, Claude Code, Lovable, Bolt, v0, ChatGPT) que vai implementar o tracking no código do seu produto.
Se você está fazendo vibe coding — codando com IA, descrevendo o que quer em linguagem natural — este artigo é o seu prompt pronto. Cole a URL desta página no chat da sua IA junto com "implemente tracking nessas telas" e pronto.
Por que SaaS precisa de tracking diferente
SaaS não é e-commerce. As diferenças que importam:
- O usuário fica logado entre sessões — você consegue identificar a mesma pessoa em todos os disparos (use
user_id) - A conversão não é o cadastro — é a primeira cobrança paga (trial → paid). Os anúncios devem otimizar para
purchase, nãolead - Receita é recorrente (MRR) — cada renovação é uma nova conversão a ser rastreada
- Existem milestones de ativação entre cadastro e pagamento (criou primeiro projeto, convidou time, integrou API) que predizem retenção e devem ser rastreados como
leadcustomizado - Upgrade/downgrade mudam o LTV — rastreie como
purchasecom novotransaction_id
Mapa: funil SaaS → eventos canônicos do Track Combo
| Etapa do funil SaaS | Evento Track Combo | Quando disparar |
|---|---|---|
| Visitou landing / pricing | page_view |
Automático pela tag web |
| Viu detalhe de plano | view_item |
Ao abrir modal/página de um plano específico |
| Pediu demo / capturou email | lead |
Form submit (sem pagamento ainda) |
| Criou conta (free trial) | lead (com user_id) |
POST de cadastro retornou sucesso |
| Ativou o produto (1º valor) | activate (custom event) |
Milestone de ativação (criou projeto, conectou conta etc) |
| Clicou em "Assinar plano" | initiate_checkout |
Ao entrar na tela de checkout/upgrade |
| Preencheu cartão | add_payment_info |
Antes de submeter o pagamento |
| Pagamento aprovado | purchase |
No webhook do gateway (Stripe) — server-side |
| Renovação mensal/anual | purchase (novo transaction_id) |
No webhook invoice.paid |
| Upgrade de plano | purchase (novo transaction_id, valor da diferença) |
No webhook de upgrade |
| Cancelamento | cancel_subscription (custom event) |
Opcional — útil para audiências de winback |
Por que
leadpara cadastro? Opurchasedeve ser reservado para evento de dinheiro entrando — assim o Facebook/Google otimizam para conversões pagas, não para cadastros grátis. Useleadcom bons identificadores (email, user_id) e o trial virapurchasequando o cartão for cobrado.
Pré-requisitos
- Container criado no Track Combo
- Tag web instalada em todas as páginas do produto (incluindo área logada) — ver Instalar a Web Tag
- Pixels conectados ao container (Facebook, Google Ads, GA4, TikTok…) — ver Pixels de Rastreamento
- (Recomendado) Integração com gateway para receber
purchaseserver-side — ver Integrar com Stripe ou Webhook Custom
Bloco para colar na sua IA assistente
Copie tudo abaixo e cole no Cursor/Claude/Lovable/Bolt/v0/ChatGPT junto com a instrução: "Implemente esse tracking no meu SaaS. Aqui está a estrutura do código: [cole também os arquivos relevantes]."
CONTEXTO: Implementar tracking de conversões usando Track Combo em um produto SaaS.
API EXPOSTA PELA TAG WEB:
A tag do Track Combo expõe window.tcData.executeEvent(params) em todas as páginas. Use sempre este helper para evitar race condition no carregamento:
function dispararEvento(params) {
if (window.tcData && typeof window.tcData.executeEvent === 'function') {
window.tcData.executeEvent(params);
} else {
setTimeout(function() { dispararEvento(params); }, 100);
}
}
REGRAS GERAIS:
- Use sempre nomes de eventos em snake_case minúsculo: purchase, lead, initiate_checkout, add_payment_info, view_item, page_view
- NÃO use Purchase, InitiateCheckout, AddPaymentInfo — esses nomes do Facebook não funcionam, a tag converte automaticamente
- value deve ser número (197.00), nunca string ("R$ 197,00")
- currency em ISO-4217 (BRL, USD, EUR)
- email e telefone vão em user_data em texto puro (são hasheados no servidor)
- doc é o documento universal (CPF, CNPJ, passaporte, tax ID) — sempre use o nome "doc", nunca "cpf"/"cnpj"/"document"
- user_id deve ser o ID interno do usuário no seu sistema — passe em TODOS os eventos após o cadastro para amarrar o funil
EVENTOS A IMPLEMENTAR EM UM SAAS:
1) CADASTRO (free trial / free plan)
Onde: callback de sucesso do POST /signup
dispararEvento({
event: 'lead',
user_data: {
email: '<email do usuário>',
first_name: '<primeiro nome se houver>',
user_id: '<ID interno do usuário recém-criado>'
}
});
2) IDENTIFICAR USUÁRIO LOGADO EM TODAS AS PÁGINAS
Onde: após autenticação, em um efeito global (App.tsx, layout raiz)
Importante: como o usuário está logado, todos os eventos seguintes devem incluir user_data com email e user_id. Crie um helper:
function dispararEventoComUsuario(params) {
const user = getCurrentUser(); // sua função existente
if (user) {
params.user_data = {
email: user.email,
user_id: String(user.id),
first_name: user.firstName,
last_name: user.lastName,
...params.user_data
};
}
dispararEvento(params);
}
Use dispararEventoComUsuario em todos os eventos a partir daqui.
3) ATIVAÇÃO (milestone de primeiro valor)
Onde: depois que o usuário cumpre o evento de ativação do seu produto (criou primeiro projeto, conectou primeira conta, importou primeiros dados etc)
Dispare apenas UMA VEZ por usuário — salve em localStorage ou no backend.
Use um nome de evento não-canônico (ex: 'activate') para ele cair como Custom Event no Facebook/Google e não se misturar com 'lead' do cadastro.
dispararEventoComUsuario({
event: 'activate',
activation_milestone: '<nome do milestone, ex: first_project_created>'
});
4) VIEW PLANO (página de pricing)
Onde: ao renderizar a página de pricing/planos
dispararEventoComUsuario({
event: 'view_item_list',
items: [
{ item_id: 'starter', item_name: 'Plano Starter', price: 29.00 },
{ item_id: 'pro', item_name: 'Plano Pro', price: 79.00 },
{ item_id: 'business', item_name: 'Plano Business', price: 199.00 }
]
});
5) INICIOU CHECKOUT (clicou em "Assinar")
Onde: ao entrar na tela de checkout / abrir modal de pagamento
dispararEventoComUsuario({
event: 'initiate_checkout',
value: <preço do plano>,
currency: 'BRL',
items: [
{ item_id: '<plan_id>', item_name: '<nome do plano>', price: <preço>, quantity: 1 }
]
});
6) PREENCHEU CARTÃO
Onde: antes de submeter o pagamento (ex: onSubmit do form de cartão, antes da chamada à API)
dispararEventoComUsuario({
event: 'add_payment_info',
value: <preço do plano>,
currency: 'BRL',
payment_type: 'credit_card',
items: [
{ item_id: '<plan_id>', item_name: '<nome do plano>', price: <preço>, quantity: 1 }
]
});
7) COMPRA APROVADA — SERVER-SIDE (RECOMENDADO)
NÃO dispare purchase no frontend. Configure a integração com o gateway (Stripe, Hotmart, Kiwify etc) no painel do Track Combo. O webhook do gateway envia o purchase server-side com user_ip/user_agent capturados, o que dá muito mais qualidade no Facebook CAPI e Google Enhanced Conversions.
Se sua integração for via Webhook Custom, o payload server-side é:
POST <url do webhook custom>
{
"event": "purchase",
"event_id": "<subscription_id ou invoice_id — usar o mesmo em renovações para deduplicação>",
"data": {
"user_data": {
"email": "<email>",
"user_id": "<id interno>",
"first_name": "<nome>",
"last_name": "<sobrenome>",
"doc": "<documento se houver>",
"user_ip": "<IP capturado no checkout>",
"user_agent": "<user-agent capturado no checkout>"
},
"tracking": {
"fbc": "<cookie _fbc>",
"fbp": "<cookie _fbp>",
"gclid": "<cookie ou param gclid>",
"tc_code": "<cookie tc_code se existir>"
},
"custom_data": {
"transaction_id": "<subscription_id ou invoice_id>",
"value": <MRR ou valor cobrado>,
"currency": "BRL",
"items": [
{ "item_id": "<plan_id>", "item_name": "<plano>", "price": <preço>, "quantity": 1 }
]
}
}
}
IMPORTANTE para SaaS server-side:
- No checkout, leia os cookies _fbc, _fbp, _ga, tc_code do navegador e salve junto com a assinatura no banco
- No webhook que dispara o purchase, recupere esses valores e mande no campo tracking
- user_ip e user_agent também devem ser capturados no checkout e salvos
- Sem isso, o Facebook CAPI não consegue casar a venda com o anúncio que originou a conversão
8) RENOVAÇÃO MENSAL/ANUAL
Mesma estrutura do purchase acima, mas com event_id diferente (use o invoice_id da renovação). O Track Combo identifica como uma nova conversão.
9) UPGRADE DE PLANO
Dispare um novo purchase com event_id do upgrade e value = (valor do novo plano − valor do plano anterior) prorrateado, ou o valor cheio se for cobrança imediata.
10) CANCELAMENTO (opcional — útil para audiências de winback)
Use um nome de evento não-canônico para virar Custom Event nos pixels.
dispararEventoComUsuario({
event: 'cancel_subscription',
cancel_reason: '<motivo se coletado>'
});
VALIDAÇÃO:
Após implementar, abra o site em uma aba, pressione F12 e rode no console:
window.tcData.events
Você deve ver todos os eventos disparados na sessão. Para debug visual flutuante, adicione ?tc_debug=1 à URL.
DOCUMENTAÇÃO COMPLETA:
- Lista canônica de eventos e parâmetros: https://trackcombo.com/help/eventos-e-parametros/
- API window.tcData.executeEvent: https://trackcombo.com/help/disparar-eventos-customizados/
- Webhook Custom (server-side): https://trackcombo.com/help/integrar-custom/
Implementação manual (passo a passo)
Se preferir implementar à mão sem IA, siga esta ordem.
1. Instale a tag em todas as páginas (incluindo a área logada)
Diferente de e-commerce (onde a tag vai só na loja pública), em SaaS a tag precisa estar na landing e na app logada. Coloque o snippet no <head> do layout raiz da sua SPA (ex: App.tsx, _app.tsx, layout.tsx).
Ver Configurar Tag Web.
2. Identifique o usuário logado em todas as páginas
Crie um helper global que injeta user_data em todo evento:
function dispararEventoComUsuario(params) {
const user = getCurrentUser(); // sua função existente
if (user) {
params.user_data = {
email: user.email,
user_id: String(user.id),
first_name: user.firstName,
...(params.user_data || {})
};
}
if (window.tcData && typeof window.tcData.executeEvent === 'function') {
window.tcData.executeEvent(params);
} else {
setTimeout(() => dispararEventoComUsuario(params), 100);
}
}
O user_id aqui é crucial: ele amarra todos os eventos da mesma pessoa no Facebook CAPI (external_id), Google Enhanced Conversions e GA4 — mesmo quando o usuário troca de navegador ou aceita cookies em outro dia.
3. Dispare lead no cadastro grátis
No callback de sucesso do POST /signup:
dispararEventoComUsuario({
event: 'lead',
user_data: {
email: novoUsuario.email,
user_id: String(novoUsuario.id),
first_name: novoUsuario.firstName
}
});
4. Marque a ativação (milestone de primeiro valor)
Quando o usuário cumpre o evento que prediz retenção (criou o primeiro projeto, importou primeiros dados, conectou primeira integração), dispare uma única vez usando um nome de evento não-canônico — assim ele cai como Custom Event nos pixels e fica separado do lead do cadastro:
if (!localStorage.getItem('tc_activated')) {
dispararEventoComUsuario({
event: 'activate',
activation_milestone: 'first_project_created'
});
localStorage.setItem('tc_activated', '1');
}
Como activate não é um nome canônico, a tag o envia como Custom Event sem renomear — ideal para criar audiências específicas no Facebook/Google ("usuários ativados últimos 7 dias", por exemplo).
5. Dispare initiate_checkout ao entrar no checkout
dispararEventoComUsuario({
event: 'initiate_checkout',
value: plano.preco,
currency: 'BRL',
items: [
{ item_id: plano.id, item_name: plano.nome, price: plano.preco, quantity: 1 }
]
});
6. Dispare add_payment_info antes de submeter o pagamento
dispararEventoComUsuario({
event: 'add_payment_info',
value: plano.preco,
currency: 'BRL',
payment_type: 'credit_card',
items: [
{ item_id: plano.id, item_name: plano.nome, price: plano.preco, quantity: 1 }
]
});
7. purchase deve vir do webhook do gateway — não do frontend
Esta é a diferença de qualidade #1 entre tracking de SaaS amador e profissional. Conectar o webhook do gateway no Track Combo te dá:
- Sinal server-side (não bloqueado por iOS/AdBlock)
- Captura confiável de
user_ipeuser_agent - Dedup com
event_id=subscription_id(renovações não duplicam) - Disparo mesmo quando o cliente fecha o navegador
Integre seu gateway:
- Stripe — disparo automático em
invoice.paid(cobre primeira venda + todas as renovações) - Webhook Custom — para gateways sem integração nativa
- Hotmart, Kiwify, Hubla — caso use checkout brasileiro
8. Propague atribuição (UTMs + click IDs) até o webhook
Para o purchase server-side casar com o anúncio que originou a conversão, no momento do checkout capture os cookies do navegador e salve junto com a assinatura no seu banco:
function capturarCookie(nome) {
const m = document.cookie.match(new RegExp('(^|; )' + nome + '=([^;]+)'));
return m ? decodeURIComponent(m[2]) : null;
}
const trackingSnapshot = {
fbc: capturarCookie('_fbc'),
fbp: capturarCookie('_fbp'),
gclid: capturarCookie('gclid') || new URLSearchParams(location.search).get('gclid'),
gcl_au: capturarCookie('_gcl_au'),
ga: capturarCookie('_ga'),
ttclid: capturarCookie('ttclid'),
tc_code: window.tcData?.tracking?.tc_code,
user_ip: '<capture no backend a partir do header>',
user_agent: navigator.userAgent
};
// Envie trackingSnapshot junto com a criação da subscription no seu backend
Quando o webhook de pagamento for disparado, recupere esses valores do banco e inclua em data.tracking e data.user_data do payload.
9. (Opcional) Rastreie cancelamento como audiência de winback
dispararEventoComUsuario({
event: 'cancel_subscription',
cancel_reason: motivoDoCancelamento
});
cancel_subscription não é evento canônico — cai como Custom Event nos pixels. No Facebook, crie uma audiência customizada "Cancelaram últimos 90 dias" para campanhas de retenção.
Boas práticas específicas para SaaS
Não otimize anúncios para lead (cadastro grátis). O algoritmo vai buscar cadastros baratos — incluindo lixo. Otimize para purchase desde o início (mesmo que demore para acumular volume). Use o lead como audiência de retargeting, não como conversão de otimização.
Use event_id igual ao subscription_id ou invoice_id. Isso garante idempotência: se o webhook do gateway dispara duas vezes (acontece), o Track Combo deduplica. E se você também dispara purchase no frontend (não recomendado, mas comum), os dois disparos com mesmo event_id viram um único evento.
Em planos com trial, o purchase deve disparar quando a primeira cobrança for aprovada — não no trial_will_end. O Stripe envia customer.subscription.trial_will_end 3 dias antes da cobrança; ignore esse webhook para tracking. Use só invoice.paid ou payment_intent.succeeded.
Inclua user_id em todos os eventos pós-cadastro. É o único identificador que cruza dispositivos com 100% de confiabilidade. O Facebook e Google usam para construir o gráfico de atribuição mesmo quando cookies expiram.
MRR vs receita anual: passe o valor da cobrança real. Se um cliente assinou plano anual de R$ 999/ano, o purchase deve ter value: 999.00, não value: 83.25 (mensalizado). O Facebook calcula ROAS sobre o value enviado.
Cuidado com email em domínios @company.com. Muitos SaaS B2B têm email do tipo usuario@empresa-cliente.com.br. O hash desse email no Facebook é match-rate baixo. Reforce sempre com user_id (que é estável) e, quando possível, doc (CPF/CNPJ do contratante).
Perguntas frequentes
Posso usar só Google Analytics 4 e pular o Track Combo?
Pode, mas perde server-side. O GA4 sozinho não envia para Facebook CAPI, TikTok Events API, Pinterest Conversions API ou Google Ads Enhanced Conversions automaticamente. O Track Combo recebe os eventos uma vez e distribui server-side para todos os pixels conectados, com hashing de PII conforme cada plataforma exige.
Como rastrear free-to-paid de planos com trial sem cartão?
Funciona igual: dispare lead no cadastro, initiate_checkout quando o usuário entra na tela de upgrade, add_payment_info quando preenche o cartão, e purchase server-side quando o webhook do gateway confirma a primeira cobrança.
Devo rastrear page_view dentro da app logada?
A tag dispara automaticamente. Mas eventos de pageview dentro do produto raramente são úteis para otimização de mídia — eles inflam o consumo de hits sem agregar sinal de conversão. Se sua app logada tem milhões de pageviews/mês, considere instalar a tag apenas em páginas-chave (pricing, checkout, signup, ativação) ao invés de globalmente.
Como tratar usuários que se cadastram via Google/Apple SSO (sem senha)?
Igual ao cadastro normal. O email vem do provedor de SSO. Importante: certifique-se de que o dispararEvento('lead') roda após o SSO retornar o usuário (no callback), não no clique do botão "Login com Google" — caso contrário você dispara antes de ter o email.
Posso disparar eventos do backend Node/Python diretamente sem usar webhook?
Pode — qualquer requisição POST com JSON pro endpoint do Webhook Custom funciona. É o caminho recomendado quando você não usa um gateway com integração pronta. Ver Webhook Custom.
Onde vejo se os eventos estão chegando?
No painel do Track Combo, acesse Container → Eventos. No navegador, F12 → Console → window.tcData.events mostra o histórico da sessão. Adicione ?tc_debug=1 à URL para abrir o debug visual flutuante.
Ver também
- Disparar Eventos Customizados via JavaScript — API completa do
window.tcData.executeEvent - Eventos e Parâmetros — Referência — fonte da verdade de eventos e campos aceitos
- Integrar com Webhook Custom — disparar
purchaseserver-side do backend - Integrar com Stripe — captura automática de assinaturas e renovações
- Configurar Pixel do Facebook — CAPI essencial para tracking server-side