📦 Containers

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ão lead
  • 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 lead customizado
  • Upgrade/downgrade mudam o LTV — rastreie como purchase com novo transaction_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 lead para cadastro? O purchase deve ser reservado para evento de dinheiro entrando — assim o Facebook/Google otimizam para conversões pagas, não para cadastros grátis. Use lead com bons identificadores (email, user_id) e o trial vira purchase quando o cartão for cobrado.

Pré-requisitos

  1. Container criado no Track Combo
  2. Tag web instalada em todas as páginas do produto (incluindo área logada) — ver Instalar a Web Tag
  3. Pixels conectados ao container (Facebook, Google Ads, GA4, TikTok…) — ver Pixels de Rastreamento
  4. (Recomendado) Integração com gateway para receber purchase server-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_ip e user_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

saastrackingvibe codingiacursorlovableboltclaudesignupcadastrotrialmrrsubscriptionassinaturaativaçãochurnfunilconversãob2b