Arquitetura Server-Side: Estruturação de Payloads HTTP para a Meta Graph API
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:
- Mesmo valor — o
event_idenviado pelo Pixel (viafbq('track', 'Purchase', {}, { eventID: 'order_7829a3f1c' })) deve ser idêntico ao enviado pelo CAPI no payload JSON. - Mesmo nome de evento —
event_namedeve ser exatamente igual nos dois canais. - 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.