
Si tengo que resumirlo en una línea: para que una campaña no llegue tarde, no duplique mensajes y no rompa consentimientos, tengo que definir qué evento mando, qué campos mínimos lleva y si sale en tiempo real o por lote.
Yo lo bajaría así, sin vueltas:
delivered, opened, clicked y replied.order.paid, checkout.abandoned, consent_updated y eventos de descuento.event_id y tratar reintentos como idempotentes.timestamp en ISO 8601 con -03:00, canal, cliente, campaña y objeto de comercio.whatsapp_opt_in, email_opt_in o sms_opt_in no están en true, yo no enviaría nada.order.paid, hay que frenar cualquier recuperación pendiente de ese checkout_id.Dicho de otro modo: no alcanza con disparar eventos. También tengo que mandar el contexto correcto: contacto, UTMs, montos en AR$, estado del pedido, descuento y consentimiento por canal. Si falta una de esas piezas, la atribución queda floja, el recupero pierde timing y soporte trabaja a ciegas.
Para verlo de un vistazo, este es el mapa corto:
| Grupo | Qué mando | Cuándo |
|---|---|---|
| Engagement | delivered, clicked, replied | Tiempo real |
| Engagement | opened | Lote corto cada 1–5 min |
| Comercio | order.paid, , |
checkout.abandonedconsent_updated| Tiempo real |
| Comercio | discount.created, discount.updated, discount.deleted | Tiempo real |
| Ajustes | order.cancelled, order.refunded, order.fulfilled | Según flujo, pero sin demora larga |
Un dato simple: en este esquema, 4 eventos de engagement + 4 bloques de comercio ya cubren casi todo lo que un equipo de CRM necesita para atribución, recupero y supresión de envíos.
Yo leería el resto del artículo como un checklist: primero eventos, después payload mínimo, y al final reglas de ingesta, seguridad y deduplicación.
Webhooks para Campañas: Eventos, Timing y Campos Mínimos
Normalizá estos cuatro eventos de engagement: delivered, opened, clicked y replied. Estos cuatro son la base de la atribución y la automatización.
La base mínima es simple: event_type, campaign_id, message_id, customer_id, channel y timestamp (11/09/2026 16:12:30).
Desde ahí, cada evento suma sus propios campos:
delivered: agrega delivery_status (valores: sent, delivered, failed, bounced) y, si hace falta, provider_message_id del ESP o BSP.opened: suma el flag first_open (booleano) para separar la primera apertura de las repetidas. En email, también podés enviar user_agent de forma opcional. En WhatsApp e Instagram, usá la primera señal de leído o visto como equivalente de opened.clicked: requiere la url de destino normalizada. En WhatsApp e Instagram, además, sumá click_position para identificar qué botón o enlace se tocó dentro de la plantilla, por ejemplo "button_ver_oferta".replied: aporta contexto de conversación. Sumá conversation_id, reply_type (free_text, quick_reply, menu_selection, opt_out, opt_in) y, cuando la política de privacidad lo permita, el texto de la respuesta. Si alguien pide la baja, normalizalo como reply_type: opt_out y activá el flujo de consentimiento.No todos los eventos piden el mismo ritmo de envío. Algunos conviene procesarlos en el momento. Otros toleran una pequeña demora sin romper nada.
| Tipo de evento | Modo recomendado | Ventajas | Desventajas |
|---|---|---|---|
| Replied | Tiempo real | Calificación inmediata; detección de opt-out sin demora | Requiere infraestructura de alta disponibilidad |
| Clicked | Tiempo real | Captura la intención en el momento justo; habilita retargeting instantáneo | Puede generar picos durante campañas masivas |
| Delivered | Tiempo real | Validación de alcance; detección rápida de errores de entrega | Alto volumen en envíos grandes |
| Opened | Lote corto (cada 1–5 min) | Reduce la carga del sistema; deduplicación más simple | Leve demora en métricas de atención |
Dicho corto: si el evento puede disparar una acción inmediata o evitar un problema legal, mandalo en tiempo real. Si el objetivo es métrica y lectura de atención, un lote corto suele alcanzar.
Cada evento responde una pregunta de negocio distinta. Si falta uno, se te arma un agujero en medición, segmentación o cumplimiento.
| Evento | Objetivo de negocio | Campos críticos | Riesgo si se omite |
|---|---|---|---|
| Delivered | Validar alcance real | message_id, timestamp, customer_id | Se sobreestima el reach; no se detectan canales bloqueados |
| Opened | Medir atención | campaign_id, first_open, timestamp | Sin datos de tasa de apertura; optimización de asunto a ciegas |
| Clicked | Capturar intención | url, campaign_id, customer_id | Retargeting roto; pérdida de atribución y datos de conversión |
| Replied | Calificar leads / detectar opt-out | conversation_id, reply_type, timestamp | Riesgo legal por ignorar bajas; oportunidades de venta perdidas |
Con estas señales cubrís atribución y capacidad de respuesta. El siguiente bloque define carrito, pedido y consentimiento.
Los eventos de engagement del bloque anterior muestran cómo reaccionó el cliente a tu mensaje. Los eventos de comercio, en cambio, muestran qué hizo después dentro de la tienda.
Esa diferencia pesa más de lo que parece. Si solo mirás aperturas, clics o respuestas, la atribución queda a mitad de camino. Y, peor todavía, los flujos de recuperación pueden activarse cuando ya no corresponde. Con estas señales bien armadas, ya podés pasar al bloque de campos mínimos para que cada evento llegue con IDs, timestamps y deduplicación.
Cada parte del recorrido de compra genera un evento distinto. Y cada uno cumple una función puntual dentro de la lógica de campaña.
Pedidos
| Evento | Momento | Campos mínimos | Uso |
|---|---|---|---|
order.created | Pedido generado (sin pago confirmado) | order_id, order_number, customer_id, status | Notificaciones transaccionales; no atribuir ingresos |
order.paid | Confirmación de pago | order_id, order_number, customer_id, status, total, currency: "ARS", discount_total, shipping_total, utm_source, utm_medium, utm_campaign | Atribución de ingresos; inicio de flujo postcompra |
order.fulfilled | Envío confirmado | status, items, datos de envío | Campañas postcompra: reseña o venta cruzada |
order.cancelled / order.refunded | Cancelación o reintegro | Motivo, montos en ARS | Ajuste de atribución y LTV; supresión de mensajes sobre esa compra |
order.created sirve para avisos transaccionales, pero no para contar ingresos. Ese corte hay que hacerlo sin vueltas. El evento que sí marca la venta es order.paid, porque confirma el pago y habilita tanto la atribución como el arranque del flujo postcompra.
Después aparecen order.fulfilled, order.cancelled y order.refunded. El primero sirve para campañas de reseña o venta cruzada. Los otros dos corrigen la película: ajustan atribución, LTV y frenan mensajes ligados a una compra que ya no sigue vigente.
Carrito
| Evento | Momento | Campos mínimos | Uso |
|---|---|---|---|
cart.created / cart.updated | Creación o modificación del carrito | cart_id, customer_id (si se conoce), items, total | Medir intención y producto de interés |
checkout.started | Inicio del checkout | checkout_id, cart_id, customer_id | Abre ventana corta de recuperación |
checkout.abandoned | Inactividad de 30–60 min tras iniciar checkout | checkout_id, cart_id, customer_id, email, phone (+54), utm_source, utm_medium, utm_campaign | Disparo principal de recuperación por WhatsApp o email |
En carrito, la lógica cambia. cart.created y cart.updated te ayudan a medir intención y a entender qué productos interesan. checkout.started marca el paso a una instancia mucho más caliente: desde ahí se abre una ventana corta para recuperar la compra si la persona se frena.
El evento más sensible es checkout.abandoned. Se dispara por inactividad de 30 a 60 min después de iniciar el checkout y funciona como gatillo principal para recuperación por WhatsApp o email. Por eso tiene que llegar con datos de contacto y UTMs. Si no, el flujo sale, pero sale medio ciego.
Regla de deduplicación clave: si llega
order.paidpara uncheckout_iddado, cancelá de inmediato cualquier mensaje de recuperación pendiente para ese checkout.
Esa regla evita uno de los errores más molestos en automatización: mandar “terminá tu compra” a alguien que ya pagó.
Después de pedidos y carrito, hay dos grupos de eventos que ordenan el resto: descuentos y consentimiento. Son los que evitan envíos inválidos o códigos que ya no corresponden.
Descuentos disparan el alta, cambio o baja de un código activo:
discount.created → alta de descuento: discount_id, code, value, type ("percentage" o "fixed_amount"), currency: "ARS", start_date, end_date, usage_limit, used_countdiscount.updated → cambio en condiciones o vigencia: mismos camposdiscount.deleted → baja del código: retiralo de cualquier envío activoSi trabajás con cupones de uso único, sumá customer_id y chequeá used_count antes de meter ese código en un envío. Parece un detalle chico, pero evita mandar un cupón vencido o ya consumido.
En consentimiento, el evento central es consent_updated. Acá no hay mucho margen para improvisar: este evento tiene que procesarse antes de cualquier envío.
Campos mínimos: customer_id, whatsapp_opt_in, email_opt_in, sms_opt_in (si aplica), consent_timestamp (ISO-8601, offset -03:00), opt_in_source ("checkout", "subscription_form", "WhatsApp conversation", "Instagram DM"), email, phone con código de país, preferred_language: "es-AR".
La regla es simple: no dispares mensajes si el flag del canal no está en true. Si no respetás eso, el problema no es solo técnico; también pega de lleno en la calidad del envío y en el vínculo con el cliente.
| Evento | Disparo | Uso |
|---|---|---|
order.paid | Confirmación de pago | Atribución de ingresos; inicio de flujo postcompra |
checkout.abandoned | Inactividad de 30–60 min | Recuperación por WhatsApp o email |
consent_updated | Opt-in u opt-out en cualquier canal | Habilitación o bloqueo inmediato de envíos |
discount.created / discount.updated / discount.deleted | Alta, cambio o baja de un descuento | Activación, actualización o retiro de códigos |
Si tenés que arrancar por un set corto, arrancá por este. Con order.paid, checkout.abandoned, consent_updated y los eventos de descuento ya cubrís lo más urgente: atribución, recuperación y supresión.
Después, cuando eso funcione bien, sumá order.cancelled, order.refunded y order.fulfilled como ampliación. A partir de ahí, el paso siguiente es validar el cuerpo del webhook mínimo y las reglas de ingesta.
Con los eventos de comercio ya definidos, el paso que sigue es simple de decir y fácil de romper en la práctica: hacer que cada webhook de order.paid, checkout.abandoned y consent_updated llegue con los campos correctos. Un evento puede dispararse perfecto, pero si el payload viene mal armado, la atribución se rompe igual que si ese evento nunca hubiera llegado.
Acá el foco pasa por tres filtros: campo mínimo, deduplicación y seguridad.
Hay dos niveles claros: mínimo operativo y completo de analytics.
| Campo | Mínimo | Recomendado | Uso |
|---|---|---|---|
event_id | ✓ | ✓ | Clave de idempotencia; sin él no podés deduplicar |
event_type | ✓ | ✓ | Define qué lógica ejecutar (order.paid, checkout.abandoned, etc.) |
timestamp | ✓ | ✓ | Orden cronológico y ventanas de atribución |
channel | ✓ | ✓ | Diferencia WhatsApp, email, Instagram DM, SMS (ideal para experiencias de compra automatizadas) |
customer_id o recipient_id | ✓ | ✓ | Une el evento al perfil del cliente o destinatario |
campaign_id | ✓ | ✓ | Atribuye el evento a una campaña específica |
order_id / cart_id | ✓ | ✓ | Conecta el evento con el objeto de comercio |
template_id | - | ✓ | Identifica qué mensaje o flujo generó el evento |
utm_source, utm_medium, utm_campaign | - | ✓ | Atribución multicanal y cálculo de ROI |
segment | - | ✓ | Compara rendimiento entre audiencias |
first_open / first_click | - | ✓ | Separa engagement único de repeticiones |
order_value_ars | - | ✓ | Ingresos en moneda local para LTV e ingresos por campaña |
Si solo pudieras volver obligatorio un campo además del evento, elegí event_id. Ese campo sostiene la idempotencia y evita que el mismo evento se procese dos veces. Sin eso, todo lo demás queda en terreno inestable.
Con ese mínimo ya definido, toca fijar cómo se manejan los IDs y los timestamps.
Usá IDs globalmente únicos e inmutables. No los derives de teléfono, texto ni etiquetas localizadas. Eso hoy puede parecer práctico, pero después te trae choques, cambios de formato y dolores de cabeza.
Para timestamps, enviá ISO 8601 con offset: 2026-09-11T14:35:22-03:00.
La regla práctica es esta:
Dicho más directo: el ID del evento tiene que identificar ese evento y solo ese evento. Y los demás IDs tienen que apuntar siempre al mismo objeto, sin depender del canal ni de cómo se lo muestre al usuario.
Sin consentimiento válido, no hay envío. Sin event_id, no hay ingesta confiable.
Reglas operativas:
event_id. Mantené una tabla o caché con los IDs ya procesados y, si llega un ID repetido, respondé 200 OK sin ejecutar la lógica de negocio. Si el event_id no está disponible, combiná recipient_id + event_type + object_id + una ventana corta de tiempo como regla secundaria.200 OK rápido y procesá en asíncrono. Para reintentos: 2xx es éxito, 4xx es error del cliente que no se reintenta, 5xx es falla temporal que sí se reintenta con backoff exponencial y un tope de intentos.La precisión de una campaña depende de señales completas y limpias.
Si los campos mínimos ya están definidos, el cierre operativo pasa por esto:
delivered, opened, clicked, replied, order.paid, [checkout.abandoned](https://burbuxa.com/blog/7-estrategias-de-recuperacion-de-carritos-abandonados/), consent_updated, discount.created, discount.updated, discount.deleted y order.refunded / order.cancelled tienen que fluir como un solo sistema.event_id: tratá cada reintento como idempotente.Con esa base, el último filtro es definir cuándo procesar cada señal. Ahí suele jugarse buena parte del resultado.
Con estas señales, Burbuxa sincroniza en tiempo real pedidos, clientes, stock y políticas de la tienda.
La diferencia, casi siempre, está en esos detalles.
Conviene arrancar por los webhooks de pagos exitosos. Así no perdés información en tiempo real y podés disparar campañas o seguimientos con mucha más precisión.
Poné el foco en eventos como payment_intent.succeeded o charge.succeeded. Son los que te avisan, sin vueltas, que el pago salió bien.
Si tu tienda está en Shopify, sumá también webhooks de pedidos, como order.created y los cambios de estado. Eso te ayuda a alinear el mensaje de la campaña con lo que está pasando con el pedido.
Dicho de forma simple: no da lo mismo hablarle a alguien que acaba de pagar que a alguien cuyo pedido ya fue enviado. Si conectás esos eventos desde el principio, el mensaje llega mejor y en el momento justo.
Implementá una lógica de idempotencia para que cada evento se procese una sola vez, incluso si llega repetido o fuera de orden.
Guardá el identificador único del evento, como id o transaction_id, y revisá en tu base de datos si ya fue procesado antes de ejecutar cualquier acción. Si ese identificador ya existe, descartá la solicitud repetida.
Cuando cambia el consentimiento de un cliente, Burbuxa recibe esa actualización por webhooks en tiempo real y la sincroniza al instante con la base de datos y los flujos de automatización.
Como trabaja con un perfil unificado del cliente, ese cambio se ve enseguida. Así, la comunicación por WhatsApp e Instagram respeta sus preferencias actuales y evita errores en campañas o mensajes transaccionales.