Infoproductos y productores

Hotmart en WhatsApp: bienvenida, boleto y carrito abandonado con webhook

Hotmart notifica a tu sistema por webhook con cada evento de venta. En la versión 2.0.0, el sobre trae id, creation_date, event, version y data, y el token de seguridad (hottok) llega en el encabezado X-HOTMART-HOTTOK. ZapZap tiene Hotmart en el asistente Recuperar vendas, en Fluxos: genera la URL del webhook y el flujo de mensajes, y valida el hottok cuando lo ingresas.

Actualizado el

Por qué conectar

El productor que vende por Hotmart vive de tres mensajes: la bienvenida cuando se aprueba la compra, el recordatorio a quien imprimió el boleto y no pagó, y el llamado a quien llenó el checkout y se fue. Hotmart tiene un evento para los tres (PURCHASE_APPROVED, PURCHASE_BILLET_PRINTED y PURCHASE_OUT_OF_SHOPPING_CART), pero hay una trampa con el teléfono que decide si el flujo se dispara o no, explicada abajo. Escribe solo a compradores o leads que dejaron su contacto contigo, y ofrece una forma de dejar de recibir mensajes. ZapZap conecta números por un canal no oficial y gestionado. Son R$15 por número activo al mes, prepago, con crédito de bienvenida al crear la cuenta, sujeto a la campaña vigente.

Paso a paso

  1. 1

    Crea la configuración de webhook en Hotmart en 2.0.0

    En Hotmart, abre Ferramentas y luego Webhook, y crea una configuración. Elige la versión 2.0.0 (la 1.0.0 está discontinuada y solo quien ya tenía configuración en ella puede crear nuevas), selecciona el producto y los eventos, y pega la URL. Hotmart guarda el historial de eventos hasta 60 días, lo que ayuda a reenviar una prueba.

  2. 2

    Genera la URL en ZapZap y elige el objetivo

    En Fluxos, haz clic en Recuperar vendas, elige Hotmart, el objetivo y el número. La URL, con el formato https://api.zapzapapi.com/api/webhooks/flow/{segredo}, es la que va en Hotmart. El flujo queda conectado y vinculado al número elegido, con textos de partida que usan {nome} y {produto}.

  3. 3

    Ingresa el hottok para validar el origen

    El hottok es un token único por cuenta, en Ferramentas > Webhook > Autenticação. Hotmart lo envía en el encabezado X-HOTMART-HOTTOK de todas las solicitudes y recomienda validarlo antes de procesar. Guárdalo en el asistente y ZapZap pasará a rechazar con 401 el evento cuyo hottok no coincida. Para cambiar el token hay que hablar con el soporte de Hotmart.

  4. 4

    Resuelve el teléfono antes del primer disparo

    En el carrito abandonado, data.buyer.phone trae el teléfono con el código de país. En los eventos de compra, data.buyer.checkout_phone trae el teléfono del checkout y, para compradores brasileños, el DDD viene separado en data.buyer.checkout_phone_code. ZapZap solo acepta como teléfono un valor de 10 a 13 dígitos; un número de 9 dígitos sin DDD no se detecta y el flujo responde triggered: 0. Haz una prueba y revisa el campo detectado en el editor del webhook.

  5. 5

    Prueba, lee el historial y ajusta

    Dispara una prueba desde Hotmart, mira en ZapZap la muestra recibida y en Hotmart el historial del evento. Si el teléfono no llega completo, el camino es un endpoint tuyo: concatenas DDD y número, y llamas directamente a la API de ZapZap, como en el ejemplo de abajo.

Ejemplo

// Cuando el teléfono llega sin DDD: tu endpoint los junta y envía por ZapZap
import express from 'express';
const app = express();

app.post('/hotmart', express.json(), async (req, res) => {
  if (req.get('X-HOTMART-HOTTOK') !== process.env.HOTMART_HOTTOK) return res.sendStatus(401);
  const { id, event, data } = req.body;
  res.sendStatus(200);
  if (event !== 'PURCHASE_APPROVED') return;
  if (await jaProcessado(id)) return; // tu función: UNIQUE en el id del evento

  const b = data.buyer;
  const fone = String((b.checkout_phone_code || '') + (b.checkout_phone || '')).replace(/\D/g, '');
  if (fone.length < 10) return;

  await fetch('https://api.zapzapapi.com/api/v1/' + process.env.ZAPZAP_INSTANCE + '/send/text', {
    method: 'POST',
    headers: {
      'x-api-key': process.env.ZAPZAP_API_KEY,
      'x-api-secret': process.env.ZAPZAP_API_SECRET,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      number: fone.startsWith('55') ? fone : '55' + fone,
      text: '¡Hola, ' + b.first_name + '! Tu compra de ' + data.product.name + ' fue aprobada. Si tienes cualquier duda, responde aquí.',
    }),
  });
});

Ilustrativo. Los campos y endpoints exactos están en la documentación.

Eventos 2.0.0 que importan y dónde está el teléfono

Los nombres de abajo son los de la documentación oficial de la versión 2.0.0. El campo purchase.status acompaña al evento y ayuda a filtrar, por ejemplo WAITING_PAYMENT o PRINTED_BILLET.

ObjetivoEventoTeléfono y datos útiles
BienvenidaPURCHASE_APPROVEDdata.buyer.checkout_phone y checkout_phone_code, data.buyer.first_name, data.product.name, purchase.price.value
Boleto sin pagarPURCHASE_BILLET_PRINTEDdata.purchase.payment.billet_url y billet_barcode cuando el pago es con boleto
Carrito abandonadoPURCHASE_OUT_OF_SHOPPING_CARTdata.buyer.phone (con código de país), data.buyer.name, data.product.name, data.offer.code
Atraso y cancelaciónPURCHASE_DELAYED, PURCHASE_CANCELED, PURCHASE_REFUNDEDÚtiles en suscripción; revisa el purchase.status de cada uno

Lo que dice la documentación y lo que debes probar

El evento de carrito abandonado se genera cuando la persona llena el nombre o el correo en la página de pago y se va; Hotmart hace esa verificación cada 30 minutos, así que el mensaje no llega en el segundo en que el cliente cierra la pestaña. Ten en cuenta ese retraso al escribir el texto y al elegir cuándo enviar el segundo recordatorio.

Para Pix, la documentación lista los campos pix_code, pix_qrcode y pix_expiration_date dentro de purchase.payment, pero la lista de eventos no tiene un nombre propio para Pix generado. Prueba en tu cuenta qué evento llega cuando alguien elige Pix y no paga, antes de prometer ese mensaje en tu flujo.

En un producto de afiliado, el productor debe liberar los datos del comprador; sin eso, el teléfono no llega.

Seguridad, duplicidad y qué hace Hotmart con una URL con error

  • hottok: valídalo en cada solicitud. No firma el cuerpo, es un token fijo; trátalo como secreto y no lo registres en logs.
  • Duplicidad: cada evento tiene un id único. Guárdalo e ignora las repeticiones. ZapZap, por su parte, no dispara el mismo flujo dos veces para el mismo teléfono en 10 minutos.
  • Fechas: creation_date y approved_date llegan en milisegundos desde 1970 (UTC). Conviértelas antes de compararlas con la hora local.
  • URL con error: el centro de ayuda de Hotmart informa que la configuración se desactiva si la URL registrada devuelve un error. Revisa el estado de tu configuración después de un deploy.
  • Límite de ZapZap: 120 solicitudes por minuto por webhook; por encima de eso la respuesta es 429.

Cuándo NO usar el asistente de ZapZap

No uses el asistente si el teléfono del checkout llega sin DDD en tu producto y no quieres o no puedes cambiar el checkout para exigir el número completo: en ese caso el campo no se detecta. Tampoco lo uses para disparar a toda la base de compradores antiguos, a quienes nunca se les avisó que recibirían WhatsApp: eso es campaña, no notificación, y pide consentimiento. Para reglas propias (consultar tu área de miembros, generar un enlace de acceso), prefiere un endpoint tuyo, con el ejemplo de arriba.

Fuentes y fecha de verificación

Los nombres de evento, los campos del payload, el encabezado X-HOTMART-HOTTOK, la versión 2.0.0, la regla de 30 minutos del carrito y el historial de 60 días se verificaron el 29 de octubre de 2026 en la documentación oficial de Hotmart (developers.hotmart.com), en las páginas Request events y Cart abandonment event de la versión 2.0.0 y Using Webhooks, y en el centro de ayuda de Hotmart. El comportamiento de ZapZap proviene del código del producto en producción.

Preguntas frecuentes

¿Dónde envía Hotmart el hottok en la versión 2.0.0?

En el encabezado HTTP X-HOTMART-HOTTOK de todas las solicitudes. La documentación recomienda validarlo antes de procesar. En la versión 1.0.0, discontinuada, el hottok venía como campo del cuerpo.

¿Por qué el flujo no se dispara en PURCHASE_APPROVED?

El motivo más probable es el teléfono. En data.buyer.checkout_phone el número de un comprador brasileño puede llegar sin DDD, que queda en checkout_phone_code. ZapZap solo acepta teléfonos de 10 a 13 dígitos, así que el flujo responde triggered: 0. Prueba y, si es el caso, junta los dos campos en un endpoint tuyo.

¿Con qué retraso llega el evento de carrito abandonado?

Hotmart verifica los carritos cada 30 minutos. El envío comienza cuando la persona llena el nombre o el correo en la página de pago. Planifica el mensaje para ese ritmo y evita escribir como si el cliente acabara de irse.

¿Existe un evento de Pix generado?

La lista de eventos de la versión 2.0.0 que revisamos no tiene un nombre propio para Pix generado. El payload trae pix_code, pix_qrcode y pix_expiration_date en purchase.payment. Prueba en tu cuenta qué evento llega cuando alguien elige Pix y no paga.

¿Puedo usar la versión 1.0.0 del webhook?

La propia Hotmart dice que la 1.0.0 fue discontinuada y que solo quien ya tenía configuración en ella puede crear nuevas. Para una integración nueva, usa la 2.0.0. El sobre cambia: campos como email y phone_number salen del primer nivel y pasan a estar dentro de data.buyer.

¿Hotmart reenvía el evento si mi servidor falla?

La documentación consultada no detalla la política de reintentos, pero los eventos tienen un id único y quedan en el historial hasta 60 días. Trata cada id como clave de idempotencia y vigila el estado de la configuración, porque el centro de ayuda dice que se desactiva si la URL devuelve un error.

¿Puedo escribirle a todos los que ya compraron?

No por este camino. Notificar una compra actual es una cosa; una campaña para la base antigua es otra, y pide consentimiento y cuidado con el ritmo. ZapZap es un canal no oficial y no elimina el riesgo de bloqueo.

Empieza ahora

Crea tu cuenta, conecta un número en minutos y prueba la API con calentamiento y anti-bloqueo. R$15 por número activo/mes, prepago, sin permanencia.