Antes de cambiar nada: comprueba una compra
Si parece que Meta cuenta una compra dos veces, empieza por una compra conocida y los eventos ya disponibles en Events Manager o en los registros de la integración. No desactives una integración que funciona ni hagas otro pedido de pago solo para esta comprobación.
- Enumera las fuentes. Anota qué canal nativo, apps y contenedores de GTM envían
Purchasey a qué conjunto de datos. - Compara una pareja. Para esa misma compra, anota el
eventIDdel navegador, elevent_iddel servidor y los nombres de evento. Indica si coinciden, difieren o no están disponibles; no supongas los valores que falten. - Separa la observación del diagnóstico. Dos registros recibidos o una diferencia entre los totales de Meta y Shopify no bastan para demostrar que una compra se ha contado dos veces. Conserva los detalles del evento y el periodo para revisarlos.
Si los valores difieren o una app los oculta, prepara una descripción para revisar Purchase en Meta. Así trasladas estas observaciones a la solicitud sin enviar datos de clientes ni cambiar antes la configuración.
¿Cómo empareja Meta un evento del navegador con su gemelo en el servidor?
La Conversions API de Meta está diseñada para funcionar junto al Pixel del navegador, no en lugar de él. Ambos deben describir la misma acción real desde dos puntos de vista: el Pixel se dispara desde el navegador del comprador, la Conversions API envía el mismo evento desde tu servidor.
Enviar los dos es la configuración recomendada, porque cada uno cubre los puntos ciegos del otro: el navegador capta eventos que un servidor podría perderse, el servidor capta eventos que un bloqueador de anuncios o una cookie truncada eliminan en el navegador.
El problema es que ahora Meta tiene dos registros de una sola compra. La deduplicación es el mecanismo que los vuelve a fundir en uno. Cuando funciona, obtienes la cobertura de dos fuentes y el recuento de una. Cuando falla, obtienes el recuento de dos, y todas las cifras posteriores —volumen de conversiones, coste por compra, ROAS— están equivocadas en la dirección que hace que las malas campañas parezcan buenas.
La deduplicación no es magia automática. Es una coincidencia entre claves que tú eres responsable de configurar correctamente.
¿En qué se basa Meta para deduplicar eventos de Pixel y CAPI?
El método principal de deduplicación de Meta compara dos campos entre el evento del navegador y el del servidor:
- event_id: un identificador único del evento concreto. En el Pixel se pasa como
eventID; en el payload de la Conversions API es el campoevent_id. El mismo valor, dos grafías distintas. - event_name: el evento estándar, por ejemplo
Purchase. Eleventdel Pixel debe ser igual alevent_namede la Conversions API.
Si un evento del navegador y uno del servidor comparten el mismo event_id y el mismo event_name, y Meta recibe el segundo dentro de las 48 horas siguientes al primero, se tratan como un solo evento. Según la documentación de Meta, el emparejamiento es por event id y event name juntos: uno sin el otro no vale.
Esto es lo que ocurre realmente en cada caso de fallo:
- El event_id difiere entre las dos fuentes. No hay coincidencia. Meta registra dos conversiones. Una compra se convierte en dos.
- El event_id se regenera en cada carga de página. El navegador y el servidor nunca llevan el mismo valor a la vez, así que nada coincide. Es el bug autoinfligido más común.
- Falta el event_id en una de las partes. No hay nada con lo que emparejar. Cuentan los dos.
- El event_name difiere (
Purchasefrente apurchase). Los identificadores pueden coincidir perfectamente y aun así falla, porque el nombre forma parte de la clave. - El segundo evento llega pasadas 48 horas. Fuera de la ventana, Meta ya dio por cerrado el primero; el gemelo tardío cuenta por su cuenta.
La consecuencia de negocio tiene siempre la misma forma: un recuento de conversiones inflado alimenta un ROAS inflado. Una campaña que reporta un ROAS de 4.0 mientras duplica el recuento en realidad va a 2.0. La escalas porque el panel dice que gana, y viertes presupuesto en una campaña que, en el mejor de los casos, empata. Los eventos duplicados no solo añaden ruido: premian activamente tus peores decisiones.
¿Por qué se dispara dos veces el evento Purchase de Meta en Shopify?
En Shopify, la duplicación rara vez viene de una única etiqueta mal configurada. Viene de dos sistemas que creen ser dueños del evento Purchase y no saben el uno del otro. Las combinaciones recurrentes:
- Canal nativo de Facebook e Instagram + una CAPI vía GTM o app. El canal nativo envía sus propios eventos de navegador y de servidor. Añade una segunda ruta de servidor mediante GTM o una app y ya tienes dos eventos Purchase por pedido sin identificador compartido.
- Una app de CAPI + el Pixel del tema o de Customer Events. La app envía Purchase desde el servidor, el Pixel de la tienda envía Purchase desde el navegador, y la app genera sus propios identificadores que el Pixel nunca ve.
- Dos apps de CAPI instaladas a la vez, a menudo un residuo de una configuración anterior que nunca se desinstaló. Las dos disparan; ninguna coordina.
- Pixel de checkout.liquid + Pixel de Customer Events durante una migración a medias hacia checkout extensibility, ambos supervivientes en la página de agradecimiento.
El hilo conductor: cada fuente puede ser correcta por separado y la combinación sigue duplicando tus datos, porque la deduplicación solo funciona cuando las fuentes coinciden en event_id. Dos fuentes bien portadas que nunca intercambian identificadores son peores que una, porque parecen completas mientras cuentan el doble en silencio. Si estás desenredando qué fuentes están activas, el diagnóstico de deduplicación de Meta CAPI recorre el inventario paso a paso.
Árbol de decisión de diagnóstico
Tabla comparativa — desplázate horizontalmente para ver todas las columnas
| Lo que ves en Events Manager | Causa probable | Qué está ocurriendo | Solución |
|---|---|---|---|
Dos filas Purchase por pedido, ambas «Processed», sin etiqueta «Deduplicated» | Dos fuentes sin identificador compartido | event_id ausente o distinto en cada una | Enruta ambas por una sola fuente de identificador; pasa el mismo valor al Pixel y al servidor |
| Las parejas coinciden en Test Events pero el recuento en producción sigue siendo ~2× los pedidos | event_id regenerado por carga de página | Los identificadores coinciden solo dentro de una misma carga, no entre navegador y servidor | Deriva event_id del id del pedido, no de Date.now() ni de un UUID nuevo |
| Los eventos de servidor cuentan por separado; los de navegador parecen correctos | Desajuste de event_name | El servidor envía purchase/PURCHASE, el Pixel envía Purchase | Unifica en Purchase (distingue mayúsculas) en ambos lados |
| Algunos pedidos se deduplican y otros no | Carrera fuera de la ventana de 48 horas, o reintentos intermitentes del servidor con identificadores nuevos | El gemelo tardío o recodificado cae fuera del emparejamiento | Estabiliza el identificador; asegúrate de que el servidor envía con prontitud |
| Las cifras cuadran en agregado pero el EMQ es bajo | No es un problema de deduplicación | La deduplicación está bien; el enriquecimiento de datos de usuario es escaso | Es otro asunto: consulta event match quality |
¿Cómo inspecciono la deduplicación en Events Manager?
No puedes diagnosticar esto desde el panel principal: muestra totales, y los totales ocultan el doble recuento. Ve a la fuente de eventos y abre Test Events:
- En Events Manager, selecciona tu dataset (Pixel), abre la pestaña Test Events y copia el código de prueba.
- Dispara una compra de prueba real (o usa el código de evento de prueba en el campo
test_event_codede tu payload de servidor para que aparezca también el evento de servidor). - Observa el feed en directo. Un evento emparejado correctamente aparece con un indicador «Deduplicated», y al desplegarlo se ve que se recibió desde Browser y desde Server con el mismo
event_id. - Si en cambio ves dos entradas
Purchaseseparadas —una de Browser y otra de Server— sin etiqueta de deduplicación, tus identificadores o nombres no coinciden. - Para datos históricos, abre un evento individual en Overview/el detalle del evento y comprueba el connection method. Un emparejamiento sano reporta tanto navegador como servidor; dos filas independientes por pedido son la señal.
La señal que buscas es la etiqueta literal «Deduplicated» en una pareja navegador+servidor. Su ausencia, cuando estás enviando ambas, es el bug.
Un esquema de implementación correcto
Toda la solución es una idea: genera el event_id una sola vez, ligado al pedido, y entrega exactamente la misma cadena al Pixel y al servidor. Este es un esquema técnicamente correcto.
Navegador (Pixel de Customer Events o del tema), pasando eventID como cuarto argumento a fbq:
// One id per purchase, derived from the order so it is identical on the server.
// NOT Date.now(), NOT a fresh crypto.randomUUID() on each load.
const eventId = `purchase_${order.id}`; // e.g. "purchase_4512890"
fbq('track', 'Purchase', {
value: 129.00,
currency: 'USD',
contents: [{ id: 'SKU-123', quantity: 1 }],
content_type: 'product'
}, { eventID: eventId });
Servidor (payload de la Conversions API), usando el mismo valor en el campo event_id en snake_case y el mismo event_name:
{
"data": [
{
"event_name": "Purchase",
"event_time": 1690000000,
"event_id": "purchase_4512890",
"action_source": "website",
"event_source_url": "https://store.example/checkout/thank-you",
"user_data": {
"em": ["<sha256 of lowercased, trimmed email>"],
"fbp": "fb.1.1690000000.1234567890",
"fbc": "fb.1.1690000000.AbCdEfGh"
},
"custom_data": { "value": 129.00, "currency": "USD" }
}
]
}
Tres detalles hacen todo el trabajo: las cadenas event_id son idénticas byte a byte, los nombres de evento son ambos exactamente Purchase (las mayúsculas importan) y el identificador se deriva del pedido para que una recarga de página no pueda cambiarlo. Los campos fbp/fbc/em mejoran la calidad de emparejamiento, pero no son lo que deduplica: eso es solo event_id + event_name.
Cómo verifico esto en implementaciones reales
Divulgación: me dedico a construir pipelines server-side y soy el fundador de Fixel Pixel, así que tengo un sesgo hacia las configuraciones server-side; y precisamente por eso verifico la deduplicación con recuentos, no con afirmaciones.
Mi protocolo en cada proyecto de CAPI:
- Inventario primero de todas las fuentes. Antes de tocar código, hago una lista de todos los sistemas que pueden disparar
Purchase: el canal nativo de Facebook, cualquier app de CAPI, GTM web, server GTM, los píxeles del tema. La duplicación casi siempre es una fuente extra que nadie recordaba, no una etiqueta rota. - Ejecución en paralelo y lectura de Test Events, no del panel. Disparo una compra real con un
test_event_codeen el servidor para que aparezcan los dos gemelos y luego confirmo la etiqueta literal «Deduplicated» en la pareja emparejada. - Conciliar recuentos con pedidos. Para una ventana fija comparo el recuento de
Purchaseque reporta Meta con el número real de pedidos de Shopify. Una configuración sana se acerca a 1:1; un recuento situado cerca de 2× los pedidos es la huella de un fallo de deduplicación aunque los eventos de prueba individuales se vean bien. - Confirmar que el identificador es estable. Recargo la página de agradecimiento y vuelvo a disparar para asegurarme de que el
event_idno cambia entre cargas. Si cambia, la deduplicación en producción fallará aunque una única prueba limpia haya pasado.
El paso de conciliación es el que la gente se salta, y es el único que detecta la duplicación intermitente.
Modos de fallo habituales
- Identificadores regenerados en cada carga de página.
event_idconstruido conDate.now(),Math.random()o un UUID nuevo en el render. Cada carga produce un valor nuevo, así que el gemelo del servidor nunca coincide. Dérivalo del pedido. - El dilema id-de-pedido frente a uuid-aleatorio. Un identificador derivado del pedido es estable y gratuito de reproducir en el servidor, pero es adivinable y se repite si el mismo pedido se vuelve a renderizar; normalmente está bien para la deduplicación. Un UUID aleatorio es inadivinable, pero solo funciona si lo generas una vez y lo persistes para que ambas partes lean el valor idéntico; generado dos veces, garantiza la duplicación. En la mayoría de configuraciones de Shopify es más seguro el identificador derivado del pedido.
- Deriva de mayúsculas y nombres.
purchasefrente aPurchase, o un evento personalizado en un lado y un evento estándar en el otro. - Confusión camelCase/snake_case.
eventIDen el Pixel,event_iden el payload del servidor. Enviarevent_idafbq(oeventIDen el JSON del servidor) no hace nada, en silencio. - La ventana de 48 horas. Envíos de servidor por lotes o retrasados que aterrizan más de 48 horas después del evento del navegador no se deduplicarán.
- Apps desinstaladas que siguen disparando. Una app eliminada cuyo snippet de píxel o webhook sobrevive en el tema.
Limitaciones
La deduplicación solo reconcilia el mismo evento descrito dos veces. No fusiona eventos genuinamente distintos y no rescatará una configuración en la que las dos fuentes describen cosas diferentes (por ejemplo, el navegador dispara en la página de agradecimiento mientras el servidor dispara en la captura del pago con un identificador distinto: esos son dos eventos, contados correctamente).
Además está acotada a Meta; dejar Meta limpio no hace nada por GA4 o Google Ads, que tienen sus propias reglas de recuento. Y una tasa de deduplicación perfecta no dice nada sobre la calidad de emparejamiento: puedes deduplicar impecablemente mientras envías eventos que Meta apenas puede atribuir. La deduplicación consiste en contar una vez; el emparejamiento es una disciplina aparte.
Alternativas
Si no puedes pasar un event_id compartido —por ejemplo, una app cerrada que no lo expone—, el mecanismo de reserva de Meta empareja por fbp o external_id más event_name, pero solo cuando el evento del navegador llega primero y ambas partes llevan el identificador de forma coherente. Trátalo como una red de seguridad, no como un plan.
La alternativa más duradera es arquitectónica: consolidar en una ruta de servidor, de modo que haya un único lugar dueño del identificador, y luego dejar que el Pixel del navegador lo replique. Normalmente forma parte de una configuración más amplia de tracking server-side o de Meta Conversions API, y por eso prefiero en general un solo pipeline bien instrumentado antes que tres a medio coordinar. La mecánica de ese pipeline se cubre en Tracking server-side: beneficios, límites y arquitectura.
¿Necesitas ayuda para comprobar Purchase en Meta?
Usa esta descripción para solicitar una revisión de las fuentes y los identificadores de evento. El siguiente paso es establecer qué se puede verificar y acordar cualquier trabajo de implementación. No necesitas elegir primero otra app de CAPI.