Carritos abandonados sin una tabla nueva: HogQL de PostHog
Cómo PideAí detecta carritos abandonados por tienda con una consulta HogQL sobre eventos de PostHog: sin tabla, sin cron y sin sincronizar estado.
Hace unos meses tuve que resolver algo que parecía simple: saber, para cada tienda de PideAí, cuántos clientes agregan productos al carrito y nunca terminan el pedido.
El primer instinto fue el de siempre. Una tabla abandoned_carts, un cron que corra cada hora, lógica para marcar un carrito como abandonado si pasan X minutos sin pedido, y otro job para mandar el aviso por WhatsApp. Arquitectura conocida, documentada y aburrida.
Antes de escribir la primera línea me hice una pregunta: ¿esta información ya la tengo en algún lado?
La tenía. Hacía meses que PideAí mandaba eventos a PostHog. Cada vez que un cliente agregaba un producto al carrito de una tienda, salía un evento. Cada vez que cerraba el pedido, otro. Los datos ya existían; me faltaba una forma de consultarlos juntos.
Qué es PostHog
PostHog es una plataforma open source de analítica de producto. Dicho simple: te muestra qué hacen los usuarios dentro de tu aplicación.
Cada acción que importa (ver un producto, agregarlo al carrito, abandonar el checkout, cerrar un pedido) queda registrada como un evento con sus propiedades. Con eso armas embudos, miras grabaciones de sesión y corres tests A/B sin trabajar sobre suposiciones.
La diferencia con Google Analytics es el foco. Google Analytics te dice cuántas páginas visitó alguien; PostHog te dice qué hizo exactamente dentro de tu app.
Lo que menos gente sabe, y que es la clave de todo esto, es que PostHog también te deja hacer consultas SQL sobre tus eventos desde tu propia aplicación. Lo llaman HogQL.
Lo que cuesta la solución clásica

Implementar carritos abandonados a la manera clásica no es difícil. El costo está en lo que casi nadie menciona: el mantenimiento.
Tienes que mantener el estado del carrito en tu base sincronizado con lo que pasa en el frontend. Si el usuario cierra el navegador, si vence la sesión, si hay un error de red, la tabla queda desactualizada. Los cron fallan sin avisar. Y el día que quieres un filtro nuevo, como “carritos abandonados de más de 50 dólares”, tocas queries, migraciones y jobs.
Todo eso para responder una pregunta que, en el fondo, es esta: ¿hubo un evento A que no fue seguido por un evento B?
HogQL: SQL sobre tus eventos
HogQL es el dialecto SQL de PostHog. No sirve solo para los gráficos de su interfaz: lo puedes consultar por API desde tu aplicación y usar el resultado como si viniera de tu propia base.
En PideAí, en vez de mantener una tabla de carritos por tienda, escribí una consulta que cruza dos eventos:
WITH cart_events AS (
SELECT person_id, properties.cart_value, timestamp
FROM events
WHERE event = 'product_added_to_cart'
AND properties.store_id = '{storeId}'
AND timestamp >= now() - INTERVAL {days} DAY
),
completed_orders AS (
SELECT DISTINCT person_id, timestamp
FROM events
WHERE event = 'order_placed'
AND properties.store_id = '{storeId}'
AND timestamp >= now() - INTERVAL {days} DAY
)
SELECT
count(DISTINCT c.person_id) as total_abandoned,
sum(toFloat(c.cart_value)) as total_value,
avg(toFloat(c.cart_value)) as avg_cart_value
FROM cart_events c
LEFT JOIN completed_orders o
ON c.person_id = o.person_id
AND o.timestamp > c.timestamp
AND o.timestamp <= c.timestamp + INTERVAL 24 HOUR
WHERE o.person_id IS NULL
La lógica es directa. Toma a todos los clientes que agregaron algo al carrito de una tienda y los cruza con los que hicieron un pedido en las 24 horas siguientes. Los que no aparecen en ese cruce son los carritos abandonados.
Sin tabla, sin job y sin nada que sincronizar.

Cómo queda en el código
En React, el hook que alimenta el panel de cada tienda quedó así:
export function usePostHogAbandonedCartStats(storeId: string, days = 30) {
return useQuery({
queryKey: ['abandoned-cart-stats', storeId, days],
queryFn: () => getAbandonedCartStats(storeId, days),
refetchInterval: 5 * 60 * 1000, // se actualiza cada 5 minutos
enabled: !!storeId,
});
}
Ahora cada tienda ve cuántos carritos se abandonaron, cuánto dinero quedó en ellos, el valor promedio y la tasa de recuperación, con datos que se actualizan cada cinco minutos. Y la base de datos no recibió ni una migración.

Lo que me llevo
Casi nunca hace falta más infraestructura. Lo que hace falta es hacerle mejores preguntas a la que ya tienes.
Si ya registras eventos con PostHog, Mixpanel o cualquier herramienta de analítica, probablemente ahí estén respuestas que estás buscando en otro lado. La diferencia está en si la herramienta te deja consultarla desde tu app o si solo te da un dashboard cerrado.
PostHog te deja. En PideAí eso abrió una categoría entera de funcionalidades que construimos sin tocar la base principal.
Así que te dejo la pregunta que me quedó a mí: ¿cuántas tablas de tu base existen solo porque no sabías que podías consultar ese dato en otra herramienta?