← voltar ao laboratório

Arquitetura Server-Side: Estruturação de Payloads HTTP para a Meta Graph API

Atualizado em Junho 2026

O rastreamento puramente client-side chegou ao seu limite operacional. Bloqueadores de anúncio como uBlock Origin e AdBlock já estão instalados em mais de 40% dos navegadores desktop. O Safari bloqueia cookies de terceiros por padrão desde 2020. O Firefox seguiu o mesmo caminho. O Chrome está em processo de deprecação desse modelo. E o iOS 14.5 introduziu o App Tracking Transparency, exigindo consentimento explícito para rastreamento entre apps.

O resultado prático: entre 20% e 40% dos eventos de conversão nunca chegam ao Meta via Pixel. Campanhas otimizadas com dados incompletos tomam decisões erradas. A solução estrutural é mover o rastreamento para onde bloqueadores não chegam — o servidor.


O que muda na arquitetura server-side

No modelo client-side tradicional, o Pixel do Facebook é um script JavaScript que roda no navegador do usuário. Ele captura eventos (PageView, AddToCart, Purchase) e os envia diretamente do navegador para os servidores do Meta.

No modelo server-side, o evento é capturado pela sua aplicação backend — seja Node.js, Python, PHP ou qualquer outra stack — e enviado via HTTP para o endpoint da Conversions API (CAPI). O navegador do usuário não participa dessa segunda rota.

Modelo híbrido recomendado:

Usuário → Navegador → Pixel (client-side) → Meta
                    ↓
              Servidor backend → CAPI (server-side) → Meta

O servidor não tem extensão de browser. Não tem política de privacidade de sistema operacional. Não tem cookie de terceiro. O evento chega ao Meta independente do ambiente do usuário.


Seção Técnica 1: Estrutura de um Payload JSON para a Graph API

O endpoint de envio é:

POST https://graph.facebook.com/v19.0/{PIXEL_ID}/events?access_token={TOKEN}

O corpo da requisição deve ser um JSON com o array data, onde cada objeto representa um evento. Abaixo está a estrutura completa e válida para um evento de Purchase:

{
  "data": [
    {
      "event_name": "Purchase",
      "event_time": 1717689600,
      "event_id": "order_7829a3f1c-1717689600",
      "event_source_url": "https://seusite.com.br/checkout/obrigado",
      "action_source": "website",
      "user_data": {
        "em": "9b8769a4e8d4a7e6c5a2b3f1d0e9c8b7a6f5e4d3c2b1a0f9e8d7c6b5a4f3e2d1",
        "ph": "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2",
        "external_id": "usuario_12345",
        "client_ip_address": "177.32.45.198",
        "client_user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36",
        "fbp": "fb.1.1717689000.1234567890",
        "fbc": "fb.1.1717689000.IwAR2xH8kL9pM3nQ7rS5tU1vW4xY6zA0bC2dE4fG6hI8jK0lM2nO4pQ6rS8tU"
      },
      "custom_data": {
        "currency": "BRL",
        "value": 297.90,
        "content_ids": ["SKU-001", "SKU-047"],
        "content_type": "product",
        "num_items": 2,
        "order_id": "order_7829a3f1c"
      }
    }
  ],
  "test_event_code": "TEST12345"
}

Detalhamento dos campos obrigatórios e opcionais

Campo Localização Obrigatório Descrição
event_name raiz do evento Sim Nome do evento padrão ou customizado
event_time raiz do evento Sim Unix timestamp em segundos (UTC)
event_id raiz do evento Para deduplicação Identificador único do evento
action_source raiz do evento Sim website, app, email, phone_call
em user_data Recomendado E-mail hasheado em SHA-256 (minúsculo, sem espaços)
ph user_data Recomendado Telefone hasheado em SHA-256 (só dígitos, com DDI)
fbp user_data Recomendado Cookie _fbp capturado do navegador
fbc user_data Recomendado Parâmetro fbclid da URL de entrada
currency custom_data Para Purchase Código ISO 4217 (BRL, USD, EUR)
value custom_data Para Purchase Valor numérico sem formatação

Como gerar os hashes SHA-256 corretamente

O Meta exige que e-mail e telefone sejam normalizados antes do hash. Qualquer divergência na normalização gera hashes diferentes dos produzidos pelo Pixel, o que prejudica a deduplicação e a EMQ.

import hashlib

def hash_email(email: str) -> str:
    normalized = email.strip().lower()
    return hashlib.sha256(normalized.encode()).hexdigest()

def hash_phone(phone: str) -> str:
    # Remove tudo que não é dígito, inclua DDI: 5511999998888
    digits_only = ''.join(filter(str.isdigit, phone))
    return hashlib.sha256(digits_only.encode()).hexdigest()

# Exemplo de uso
print(hash_email("Usuario@Email.com"))
# → 9b8769a4e8d4a7e6c5a2b3f1d0e9c8b7a6f5e4d3c2b1a0f9e8d7c6b5a4f3e2d1

print(hash_phone("+55 (11) 99999-8888"))
# → a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2

O campo external_id também deve ser hasheado se contiver informações pessoais identificáveis. Para IDs internos sem PII (como usuario_12345), o hash é opcional mas recomendado por consistência.


Seção Técnica 2: A Lógica de Deduplicação com event_id

Quando Pixel e CAPI operam em paralelo — que é o cenário correto — o mesmo evento de conversão é enviado por duas rotas. Sem deduplicação, o Meta contabiliza duas conversões onde ocorreu apenas uma.

O mecanismo de deduplicação do Meta usa o parâmetro event_id para identificar eventos duplicados dentro de uma janela de 48 horas. A regra é simples: se dois eventos chegarem com o mesmo event_name e o mesmo event_id, o Meta conta apenas um.

Regras de implementação do event_id

Para que a deduplicação funcione corretamente, três condições precisam ser atendidas simultaneamente:

  1. Mesmo valor — o event_id enviado pelo Pixel (via fbq('track', 'Purchase', {}, { eventID: 'order_7829a3f1c' })) deve ser idêntico ao enviado pelo CAPI no payload JSON.
  2. Mesmo nome de eventoevent_name deve ser exatamente igual nos dois canais.
  3. Janela de 48 horas — o Meta descarta duplicatas recebidas até 48 horas após o primeiro evento.

Implementação no Pixel (client-side)

fbq('track', 'Purchase', {
  value: 297.90,
  currency: 'BRL',
  content_ids: ['SKU-001', 'SKU-047'],
  num_items: 2
}, {
  eventID: 'order_7829a3f1c-1717689600'  // mesmo valor que o CAPI enviará
});

Geração do event_id no backend

import uuid
import time

def generate_event_id(order_id: str) -> str:
    """
    Gera um event_id determinístico baseado no order_id.
    Idempotente: o mesmo pedido sempre gera o mesmo event_id.
    """
    timestamp = int(time.time())
    return f"{order_id}-{timestamp}"

# Para eventos sem order_id (ex: AddToCart), use UUID v4
def generate_session_event_id() -> str:
    return str(uuid.uuid4())

Por que usar event_id determinístico

Um event_id baseado no order_id do pedido é determinístico: se o servidor processar o mesmo pedido duas vezes por falha de rede ou retry, o event_id gerado será idêntico, e o Meta descartará a duplicata automaticamente. UUIDs aleatórios, ao contrário, geram IDs diferentes em cada tentativa, podendo causar contagem dupla em cenários de retry.

Verificando a deduplicação no Events Manager

No painel do Meta Events Manager, a coluna "Received" mostra o total bruto de eventos recebidos por canal. A coluna "Deduplicated" indica quantos foram descartados por serem duplicatas válidas. Se a coluna Deduplicated estiver zerada mas você estiver enviando pelos dois canais, isso indica que os event_id não estão batendo — o Meta está contando dobrado.

Events Manager → Seu Pixel → Visão Geral → Coluna: Deduplicados

Validação e teste do payload

Antes de enviar para produção, use o parâmetro test_event_code no payload (disponível no painel do Events Manager em "Testar Eventos"). Eventos com esse parâmetro aparecem em tempo real na aba de testes sem contaminar os dados de produção.

curl -X POST \
  "https://graph.facebook.com/v19.0/SEU_PIXEL_ID/events?access_token=SEU_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "data": [{
      "event_name": "Purchase",
      "event_time": 1717689600,
      "event_id": "test_order_001",
      "action_source": "website",
      "user_data": {
        "em": "9b8769a4e8d4a7e6c5a2b3f1d0e9c8b7a6f5e4d3c2b1a0f9e8d7c6b5a4f3e2d1"
      },
      "custom_data": {
        "currency": "BRL",
        "value": 10.00
      }
    }],
    "test_event_code": "TEST12345"
  }'

A resposta esperada para um payload válido é:

{
  "events_received": 1,
  "messages": [],
  "fbtrace_id": "ABC123xyz..."
}

Qualquer campo messages preenchido indica erro de validação com a descrição exata do problema — o Meta retorna mensagens de erro bastante descritivas para facilitar o debug.


Impacto da EMQ na otimização de campanhas

A Event Match Quality (EMQ) é a métrica que o Meta usa para indicar o quão bem os dados de usuário no seu payload correspondem a perfis reais na plataforma. A pontuação vai de 0 a 10.

Uma EMQ alta significa que o Meta consegue atribuir seus eventos de conversão a usuários reais com maior precisão. Isso alimenta o algoritmo de otimização com sinais mais limpos, o que resulta em distribuição mais eficiente dos anúncios, públicos semelhantes mais qualificados e, consequentemente, redução do custo por resultado.

EMQ abaixo de 6 geralmente indica que o payload está enviando poucos campos de user_data. A estratégia para elevar a EMQ é simples: enviar o maior número possível de campos não nulos — especialmente em, ph, fbp, fbc e external_id de forma combinada.

← todos os posts Sugerir melhoria para este conteúdo →