Voltar pro blog json-p.org

Pilar3 · Rastreamento honesto de vendas Publicado7 de setembro de 2026 AutorElton

O ROAS do Meta não bate com a conta: como reconciliar

O ROAS do Meta não bate porque o painel mede outra coisa: janela, view, evento duplicado, modelagem e reembolso. O mecanismo e a reconciliação real.

Você abre o gerenciador e ele estampa ROAS 3,20. Gastou R$ 4.180, "gerou" R$ 13.400. Aí você entra no painel do gateway, filtra o mesmo período, soma o que foi aprovado, olha o saldo a receber, e não chega nem perto. O ROAS do Meta não bate com a conta, e a diferença não é de 5%. É quase metade.

Se você já escalou orçamento em cima do painel e fechou o mês no vermelho, viveu isso. O pior nem é a divergência existir. É não saber o tamanho dela. Sem o tamanho, você não sabe se corta a campanha, se aumenta o orçamento ou se o problema é só o rastreamento estar furado, e aí toda decisão do mês vira chute caro.

A parte que ninguém explica direito é que os dois números não são versões do mesmo número. São contas diferentes, com numerador diferente e denominador diferente. Depois que você enxerga isso, para de tentar fazer os painéis concordarem (não vão) e passa a construir a terceira tabela, que é a única que paga boleto.

A resposta curta: o ROAS do Meta não bate porque não é o mesmo número

O que o Meta chama de valor de conversão é a soma dos eventos de compra que ele conseguiu atribuir a um anúncio, dentro da janela dele, incluindo conversão estimada por modelagem, pelo valor bruto que você mandou no evento, sem descontar reembolso nem chargeback, e possivelmente contando a mesma venda duas vezes se pixel e CAPI não deduplicam.

O que você chama de receita é dinheiro confirmado no gateway, líquido de taxa de plataforma, comissão de afiliado, reembolso e chargeback, na data em que o pagamento caiu.

Os denominadores também não são iguais: o painel credita a conversão na data do clique no anúncio, não na data da venda, então o gasto de segunda-feira aparece ao lado de vendas que aconteceram na quinta. Comparar "gasto do dia X" com "vendas do dia X" já nasce errado.

Reconciliar não é decidir qual painel está certo. É montar uma tabela no nível da transação, com uma chave de junção que sobrevive do anúncio até o pagamento, e medir o gap dos dois lados. O resto do post é o mecanismo de cada divergência e o passo a passo dessa tabela.

As sete portas por onde a diferença entra

1. A janela de atribuição corta os dois lados

A configuração padrão do Meta hoje é 7 dias de clique mais 1 dia de visualização. Isso significa duas coisas que se cancelam mal.

Pra baixo: a pessoa clicou no anúncio no dia 1, entrou na sua lista, recebeu email, voltou e comprou no dia 12. Pro seu extrato isso é uma venda vinda do tráfego pago. Pro Meta não existe, porque caiu fora da janela de 7 dias. Quanto mais longo o ciclo de decisão do seu produto, maior esse buraco. Ticket alto e produto que precisa de aquecimento sofrem mais.

Pra cima: alguém que já ia comprar de qualquer jeito viu seu anúncio no dia anterior e converteu. Pela janela de visualização de 1 dia, o Meta reivindica essa venda. Você paga por uma conversão que aconteceria sem anúncio nenhum.

2. Nem toda conversão veio de clique (e isso mudou em março de 2026)

Aqui está a mudança que fez muita gente achar que o algoritmo quebrou. Em março de 2026 o Meta estreitou a definição de clique pra fins de atribuição: só clique em link, aquele que tira a pessoa do Facebook ou do Instagram, conta como click-through. Curtida, comentário, compartilhamento, salvar e outros cliques que não são link saíram dali e foram pra uma categoria nova, chamada engage-through attribution, que é o novo nome do que antes era engaged-view. Junto veio o encurtamento da janela de engajamento em vídeo, de 10 segundos para 5.

Efeito prático: um pedaço das suas conversões mudou de balde sem que a performance real mudasse uma vírgula. Se o seu ROAS "caiu" ou "subiu" do nada em 2026 e você não mexeu em nada, provavelmente foi isso. Antes de refazer criativo por causa de um número que despencou, cheque em qual coluna de atribuição você está olhando.

A pergunta que importa pra reconciliação: quanto do seu ROAS de painel vem de conversão que não teve nem clique em link? Se boa parte vem de view e de engajamento, esperar que aquilo bata com o extrato é ilusão. Ninguém "comprou por causa de um save".

3. O painel credita na data do clique, seu extrato na data do pagamento

O gerenciador, no padrão, atribui a conversão à data da impressão ou do clique que a originou, não à data em que a venda aconteceu. Consequência direta: o número de ontem continua mudando por dias. Você olha na segunda e vê ROAS 1,4; olha de novo na sexta e o mesmo dia virou 2,6, porque as vendas dos dias seguintes foram creditadas retroativamente naquele clique.

Quem fecha relatório na sexta e compara com o extrato do gateway está comparando um número que ainda vai crescer com um número que já parou de crescer. É a divergência mais fácil de eliminar e a que mais gente ignora.

4. Conversão modelada: o Meta estima o que o pixel não viu

Desde o ATT do iOS, bloqueador de rastreador e banner de consentimento, uma fatia grande dos eventos simplesmente não chega. O Meta não deixa o buraco em branco: ele estatisticamente modela a conversão que não conseguiu observar e reporta isso junto com as observadas, sem separar em coluna própria pro anunciante comum.

Estimativa não é linha de extrato. Ela pode até estar direcionalmente certa (serve pra o algoritmo otimizar), mas você não consegue apontar qual transação é aquela, porque ela não existe como transação. Toda conversão modelada é, por definição, irreconciliável com o gateway.

5. Pixel e CAPI contando a mesma venda duas vezes

Esse é o mais silencioso e o que mais infla ROAS. Quando você manda o mesmo evento de compra pelo pixel do navegador e pela Conversions API, o Meta precisa perceber que é a mesma ação. Ele faz isso comparando event_name e event_id, e só deduplica se o segundo evento chegar dentro de 48 horas do primeiro com aquele mesmo id.

// o MESMO id gerado uma vez por ação do usuário,
// enviado pros dois caminhos, string idêntica

const eventId = crypto.randomUUID();

// navegador
fbq('track', 'Purchase',
    { value: 350.00, currency: 'BRL' },
    { eventID: eventId });

// servidor (Conversions API), no webhook de pagamento aprovado
{
  "event_name": "Purchase",
  "event_id":   eventId,
  "event_time": 1757260800,
  "action_source": "website",
  "custom_data": { "value": 350.00, "currency": "BRL" }
}

Onde quebra na vida real: o id do navegador é gerado numa hora e o do servidor em outra, ou vem com espaço, ou com diferença de caixa alta e baixa, ou o servidor manda o id do pedido e o navegador manda um UUID. Qualquer desalinhamento e a dedup falha: uma venda, dois eventos, ROAS inflado sem aviso nenhum. Existe um fallback por fbp e external_id, mas ele só funciona quando o evento do navegador chega primeiro e o do servidor depois, então trate como rede de segurança, não como estratégia.

Vale a mesma disciplina que se usa em qualquer consumidor de webhook: um identificador estável por ação, gerado uma vez, propagado. É a mesma lógica de idempotência em webhook, só que agora o efeito colateral de errar não é mandar mensagem repetida pro cliente, é você achar que vendeu o dobro.

6. Venda que acontece fora do alcance do pixel

O Meta só sabe do que chega até ele. Fica de fora, por padrão:

7. Bruto no evento, líquido na conta

O valor que você manda no evento de compra é o preço cheio. O que cai na sua conta é o preço cheio menos taxa de plataforma, menos comissão de afiliado, menos imposto, e depois menos tudo que voltou.

Reembolso e chargeback não voltam pro painel do Meta. Uma vez contada, a conversão fica lá. No gateway ela muda de status: a Hotmart, por exemplo, trabalha com estados de transação que incluem REFUNDED, PARTIALLY_REFUNDED e CHARGEBACK, e dispara evento de webhook quando isso acontece. Se você tem produto com muito reembolso dentro da garantia, ou nicho com chargeback alto, essa diferença sozinha come um ponto de ROAS. Vale ler a lista de eventos do webhook da Hotmart e decidir quais deles rebaixam receita na sua tabela.

A reconciliação: junte transação com transação, não métrica com métrica

A regra que muda tudo: você não reconcilia dois números agregados. Você reconcilia duas listas de linhas. Uma linha por venda, uma linha por dia de gasto por anúncio, e uma chave que amarra as duas.

A chave que sobrevive: id numérico, não nome de campanha

O erro clássico é usar utm_campaign com o nome da campanha. Nome muda. Você renomeia "Fitness LP Novo" pra "Fitness LP Novo v2" no meio do mês e a série histórica quebra em duas. Pior: os macros de nome do Meta congelam o valor no momento da publicação, então depois de renomear o link continua carregando o nome antigo e você tem duas verdades na mesma planilha.

Use id numérico. O Meta tem parâmetros dinâmicos de URL que resolvem isso no nível do anúncio: {{ad.id}}, {{adset.id}}, {{campaign.id}}, além de {{placement}} e {{site_source_name}}. Eles são substituídos no clique.

https://pay.hotmart.com/SEUPRODUTO
  ?src=fb|{{ad.id}}
  &utm_source=facebook
  &utm_medium=cpc
  &utm_campaign={{campaign.id}}
  &utm_content={{ad.id}}

Detalhe que quebra join sem avisar: o src e o sck da Hotmart aceitam até 30 caracteres, o underline é reservado pelo sistema dentro do valor, e o pipe é o separador que ela entende (e que ainda separa em colunas no relatório de origem). Um id de anúncio do Meta tem 17 ou 18 dígitos, então fb|120210000000000000 cabe folgado. Um prefixo mais falante do que isso não cabe, e o valor chega truncado. Truncado significa que o join não casa e a venda vira "origem desconhecida" em vez de dar erro. Teste clicando num anúncio ao vivo e conferindo a URL final antes de confiar.

Se você é afiliado, atenção: o código que viaja no hotlink identifica quem vendeu, não de onde veio o clique. Ele não substitui o parâmetro de origem.

As três tabelas

A reconciliação inteira é um join de três coisas. Nada de exótico:

Duas disciplinas obrigatórias nessa tabela de vendas: grave o payload cru assim que ele chega, antes de qualquer regra de negócio, e nunca insira a mesma transação duas vezes porque o gateway reentregou o webhook. Sua própria tabela inflando o número é o jeito mais constrangedor de perder a discussão com o painel do Meta.

A escada de reconciliação

Feito o join, você não produz um número. Produz uma escada, e cada degrau tem uma causa nomeada. Exemplo de uma campanha real, valores redondos:

gasto no periodo                                 4.180

ROAS do painel do Meta                            3,20
  (38 compras atribuidas, R$ 13.400)

(-) 11 compras que o gateway nao tem            -3.950
(+)  6 vendas que o Meta nao contou             +2.100
--------------------------------------------------------
= receita bruta reconciliada                    11.550    ROAS 2,76

(-) 3 reembolsos + 1 chargeback                 -1.400
--------------------------------------------------------
= receita bruta confirmada                      10.150    ROAS 2,43

(-) taxa de plataforma + comissao de afiliado   -2.030
--------------------------------------------------------
= receita liquida (o que cai na conta)           8.120    ROAS 1,94

Painel: 3,20. Realidade pagável: 1,94. E, o mais importante, cada degrau da queda tem endereço. Não é "o Meta mente", é 11 conversões sem transação correspondente, 4 devoluções e a mordida da plataforma.

Os três baldes do delta

Depois do join, toda linha cai em um de três baldes, e cada balde manda você mexer num lugar diferente:

Balde 1 grande com balde 2 pequeno é um problema de instrumentação e você conserta em uma tarde. Balde 2 grande é problema de arquitetura de rastreamento e é o que dá mais dinheiro consertar, porque significa que você tem campanha lucrativa parecendo ruim no painel.

Manter essa escada atualizada sozinho, no braço, é a parte chata: virar export do gerenciador, casar com webhook do gateway, rebaixar status, fechar por período com a janela já vencida. É exatamente isso que o utm, o tracker que eu mantenho, faz por baixo do capô: carimba o ad_id na ida, recebe o webhook do gateway na volta, e devolve a escada pronta com os três baldes separados. Mas o mecanismo acima é genérico. Dá pra montar com o export do gerenciador, um endpoint de webhook e uma tabela, sem depender de mim.

O que dá errado

Fechando

O ROAS do Meta não bate com a conta porque o painel responde outra pergunta: "quanto desse resultado eu consigo reivindicar dentro da minha janela e do meu modelo". A sua pergunta é "quanto sobrou". As duas são legítimas, e nenhuma das duas sozinha manda no orçamento.

O trabalho é montar a terceira tabela: carimbar o id numérico do anúncio no parâmetro de origem, ler o webhook do gateway transação por transação, rebaixar o que voltou, e olhar os três baldes do delta em vez de olhar um número só. É trabalho de encanamento, chato, e é o que separa quem escala com dado de quem escala no susto.

Fontes primárias consultadas na data da escrita: Central de Ajuda da Meta para Empresas (configurações e janelas de atribuição; especificações dos parâmetros dinâmicos de URL), documentação da Conversions API sobre deduplicação de eventos entre pixel e servidor (event_name, event_id, janela de 48 horas), cobertura da atualização de atribuição do Meta de março de 2026 (click-through restrito a cliques em link, engage-through attribution, janela de engajamento em vídeo de 5 segundos) e Central de Ajuda da Hotmart sobre identificação de origem de vendas (src, sck, limite de 30 caracteres) e estados de transação.