Analítica de eventos con Zaraz¶
Qué mide el storefront, dónde se consulta cada evento y qué hay que saber antes de sacar conclusiones de los números. Corresponde a la HU-03.
Este documento tiene dos destinatarios: quien analiza los datos (las secciones 1 a 5) y quien toca el código (las secciones 6 y 7).
1. Los tres lugares donde mirar¶
| Dónde | Para qué sirve | Cuándo usarlo |
|---|---|---|
| Debugger de Zaraz (panel de Cloudflare) | Ver los eventos en vivo mientras se navega, con todos sus parámetros | Verificar que algo se dispara, y con qué valores |
| Jitsu | El evento completo, tal como se guarda | Contar, sumar y cruzar. Es la fuente confiable |
| GA4 | Los informes armados | Reportes habituales, con las salvedades del punto 3.2 |
Empezar siempre por el debugger de Zaraz para confirmar que el evento sale, y recién después buscarlo en Jitsu o GA4.
2. Qué se mide¶
Trece eventos, todos verificados en vivo el 2026-09-10.
El recorrido de compra¶
| Evento | Se dispara cuando | Desde qué pantalla |
|---|---|---|
Product List Viewed |
Se muestra una lista de productos | Tienda, categorías, home, relacionados |
Product Clicked |
Se hace clic en un producto de una lista | Todas las grillas |
Product Viewed |
Se abre la ficha de un producto | Ficha |
Products Searched |
Se ejecuta una búsqueda | Tienda con ?q= |
Product Added |
Se agrega al carrito | Ficha y carrito |
Product Removed |
Se saca del carrito | Carrito y desplegable del header |
Cart Viewed |
Se abre la página del carrito | Carrito |
El checkout¶
Los seis viajan con el mismo checkout_id, que es el id del carrito. Es lo
que permite medir en qué paso se cae la gente.
| Evento | Se dispara cuando |
|---|---|
Checkout Started |
Se entra al checkout con productos |
Checkout Step Viewed |
Se abre cada paso (1 Dirección, 2 Envío, 3 Pago, 4 Revisión) |
Checkout Step Completed |
Se avanza desde un paso al siguiente |
Shipping Info Entered |
Se confirma el método de envío y se avanza |
Payment Info Entered |
Se confirma el método de pago y se avanza |
Order Completed |
Se confirma la compra |
El paso 4 no emite Checkout Step Completed: completar la revisión es
comprar, y eso lo reporta Order Completed. Contarlo dos veces inflaría la
conversión.
3. Avisos que hay que leer antes de sacar conclusiones¶
Esta sección es la más importante del documento. Todo lo que sigue fue medido, no supuesto.
3.1. quantity significa dos cosas distintas¶
- En
Product Viewedy en las listas,quantityes la disponibilidad:1si el producto se puede comprar,0si está agotado. Se usó ese campo porque la lista de parámetros de Zaraz no tiene ninguno de stock. - En
Product Added,Product Removed,Cart ViewedyOrder Completed,quantityson unidades reales.
Consecuencia: no se pueden sumar los quantity de todos los eventos juntos.
Hay que filtrar por evento primero.
Además, value no multiplica por quantity cuando éste es la bandera de
stock. Si lo hiciera, todo lo agotado valdría 0 y hundiría el valor por sesión.
3.2. En GA4 el cero cambia de tipo de parámetro¶
Zaraz manda los números distintos de cero como epn. (numérico) y el cero
como ep. (texto). Medido: ep.num_cero=0 junto a epn.num_uno=1.
Consecuencia: un mismo campo (quantity, nb_hits) cae en dimensión de
texto cuando vale 0 y en métrica cuando no. Para sumar o promediar esos
campos hay que usar Jitsu, no GA4.
3.3. El debugger muestra en blanco los valores 0 y false¶
No están perdidos: en el POST real a Jitsu llegan intactos. La tabla del debugger no los renderiza. Verificado mandando el mismo dato en nueve codificaciones.
Si un campo aparece vacío en el debugger, probablemente vale cero.
3.4. El debugger no lista los eventos en orden cronológico¶
Hay que ordenar por hora antes de concluir que algo falta. Por leer la lista de arriba abajo se dieron por perdidos cuatro eventos que sí habían llegado.
3.5. En el carrito se reporta la cantidad final, no la diferencia¶
Decisión del stakeholder (2026-09-09). Pasar de 1 a 5 unidades reporta
Product Added con quantity: 5, no con 4.
Consecuencia aceptada: las unidades agregadas que sume el panel no
coinciden con el carrito. Pasar por 2, 3 y 4 reporta 9 unidades para un
carrito de 4. Para contar unidades vendidas hay que usar Order Completed, no
la suma de Product Added.
La excepción es el botón de borrar, que reporta las unidades que tenía la línea.
3.6. Shipping Info Entered subcuenta¶
El botón "Ir a pagar" del carrito lleva al paso que corresponda según el estado del carrito. Un visitante que abandona el checkout y vuelve con el envío ya elegido entra directo en el paso de pago y nunca dispara ese evento, porque vive en el botón de "Continuar a pago" y no en la URL.
Consecuencia: ese escalón del embudo va a mostrar menos gente de la que realmente pasó por él.
3.7. El precio de las listas puede no coincidir con el de la venta¶
Product List Viewed y Product Clicked reportan el precio del índice de
Algolia, que es el que la tarjeta muestra en pantalla. Cart Viewed,
Checkout Started y Order Completed reportan el de Medusa, que es el que
se cobra.
Mientras el índice esté desactualizado, los dos números difieren. Medido el
2026-09-10 con el sku CPI-673517: 1.399.000 en la lista, 1.607.141 cobrado.
No es un error de la medición — el evento reporta lo que el visitante vio — pero no se pueden comparar esos precios entre sí.
3.8. brand tiene cobertura incompleta¶
- En los eventos que salen de Medusa (ficha, carrito, checkout, venta) la cobertura es de 83%.
- En los que salen de Algolia (listas, clics) sale casi siempre vacío, porque el índice no trae la marca.
Consecuencia: un informe de "vistas por marca" sobre las listas va a estar muy incompleto. Usar los eventos de ficha en adelante.
3.9. Los ids pueden no cruzar mientras el índice esté viejo¶
El product_id es el id de variante. Un mismo sku puede tener distinto id
de variante en Algolia y en Medusa mientras el índice esté desactualizado.
Mientras tanto, cruzar por sku, que sí coincide.
3.10. Quién es quién: cfz y user_id¶
cfzidentifica el navegador. Viaja siempre, también sin sesión iniciada. Jitsu lo recibe comoanonymousId. Es el mismo id que usa el enlace de WhatsApp, así que la conversación cruza con la navegación.user_ididentifica a la persona: es el id de cliente de Medusa, y sólo viaja cuando hay sesión iniciada.
Sin user_id, el mismo cliente que mira desde el celular y compra desde la
computadora son dos visitantes distintos.
4. Los campos de producto¶
Todos los eventos con productos usan el mismo mapeo, para que el mismo producto se mida igual en toda la tienda.
| Campo | Qué es |
|---|---|
product_id |
Id de variante, no del producto padre |
name |
Título de la variante |
sku |
De la variante |
category |
Del producto |
brand |
Del producto — ver 3.8 |
variant |
Valores de las opciones unidos con " - " (ej. "Rojo"), sin la marca |
price |
Guaraníes enteros, con IVA incluido. No dividir por 100 |
quantity |
Ver 3.1 |
position |
Lugar en la lista, empezando en 1 |
value |
price × unidades reales |
Si un dato no existe, la clave no se manda. Nunca llega vacía: un "" sería
una fila (vacío) en los informes.
Un precio en 0 se omite, junto con currency y value. Un cero no es un
precio barato, es un dato que el integrador no cargó, y hundiría el ticket
promedio.
La position con paginación¶
Es la posición del listado completo, no la de la página: en la página 2 el primer producto es el 13. Si se reiniciara en cada página, el informe mostraría que lo más clickeado está siempre en las primeras posiciones — sería un artefacto del conteo.
No todas las grillas la mandan. Hoy la mandan la tienda, las categorías, la home y los relacionados; si el campo no está, es que esa grilla no la conoce.
5. Qué NO se mide¶
Order Updated,Order RefundedyOrder Cancelled. Ocurren en el admin de Medusa, no en el navegador. Necesitan un suscriptor en el backend.- Los filtros del panel lateral. El panel de Zaraz tiene los triggers
Clicked FiltersyViewed Filterscreados, pero nadie los emite todavía. Product Added to Wishlist, porque la wishlist se sacó del sitio, yViewed Promotion/Clicked Promotion, excluidos a propósito.
Errores abiertos en el panel (no son del código)¶
- La conversión "Search" de Google Ads sale con la búsqueda vacía
(
&label=&query=). El log de Zaraz lista{{ variable.bt-query }}entre los Empty values: la variable del panel no está tomando elquery, que sí llega bien y que GA4 sí mapea aep.search_term.
6. Dónde vive el código¶
| Archivo | Qué hace |
|---|---|
src/lib/analytics/zaraz.ts |
Emisor. Tiene la cola y vincula cfz y user_id |
src/lib/analytics/product-event.ts |
El único mapeo. Producto, línea de carrito, pedido y registro de Algolia |
src/lib/util/product-attributes.ts |
Marca, categoría, opciones y stock. Compartido con el JSON-LD |
src/modules/analytics/components/ |
Un componente por evento de pantalla |
Regla que sostiene todo: un solo mapeo para todos los eventos. Si cada uno armara el suyo, el mismo producto llegaría distinto según por dónde pasó el visitante y el embudo no cerraría.
La cola de eventos¶
Cloudflare inyecta Zaraz después de que la página carga, así que los eventos
que se disparan al abrir salían antes y se perdían. Medido: la primera tanda
de Product List Viewed (posiciones 1 a 15) nunca llegaba.
Por eso emitirEvento() guarda en una cola lo que no puede enviar y lo manda
cuando Zaraz aparece. Guarda hasta 30 eventos, sondea cada 300 ms y descarta a
los 20 segundos, para el visitante con bloqueador donde Zaraz no va a aparecer
nunca.
El despacho es del lado del servidor¶
Zaraz recibe el evento en el navegador pero lo despacha desde el worker de
Cloudflare ("channel": "server"). Por eso se puede emitir justo antes de una
navegación dura sin que se pierda.
7. Cómo depurar¶
En desarrollo la consola registra todos los eventos, lleguen o no a Zaraz.
Buscar [zaraz] en la consola del navegador.
En un ambiente desplegado, agregar ?zarazdebug a la URL una vez. Queda
guardado en localStorage y desde ahí la consola registra igual.
Sirve para separar dos casos que se parecen: si el evento aparece en la consola pero no en el debugger, el problema es del envío; si no aparece en la consola, el problema es del código.
Para probar el checkout¶
- Sin productos en el carrito,
/checkoutdevuelve "Página no encontrada", y no emite ningún evento. Es el comportamiento correcto, no un error. - Hay que recorrerlo con los botones. Pegar
?step=paymento?step=reviewen la URL no disparaShipping Info EnteredniPayment Info Entered: esos eventos viven en los manejadores de los botones, a propósito, para que no reporten datos que nadie ingresó. - Para repetir una prueba sin comprar de nuevo: desde Revisión, tocar Editar en Envío y volver a avanzar.
Para volver a emitir un Order Completed¶
Se deduplica por pedido en localStorage, porque esa página se recarga y se
vuelve a abrir desde el correo. Para forzar una nueva emisión, borrar la clave
zaraz_order_completed:<id del pedido>.