Facturas, Pix y boleto

Eduzz en WhatsApp: factura pagada, boleto y carrito con x-signature validado

Eduzz envía webhooks de MyEduzz y del checkout, cada uno con id, nombre del evento, bloque data y sentDate, y firma cada envío con el encabezado x-signature, un HMAC-SHA256 del cuerpo. ZapZap tiene Eduzz en el asistente Recuperar vendas: genera la URL del webhook y el flujo de mensajes, y valida el x-signature cuando ingresas la clave secreta.

Actualizado el

Por qué conectar

Eduzz tiene dos eventos que le facilitan la vida a quien recupera ventas: la factura abierta (invoice_opened) ya trae la URL de recuperación (saleRecoveryUrl) y el enlace del boleto (bankslipUrl), y el carrito abandonado (cart_abandonment) trae la URL del checkout para que la persona vuelva. Con eso el mensaje no necesita búsquedas extra: el enlace ya está en el payload. Escribe solo a quien inició una compra, con 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 una clave de acceso en Eduzz

    En la Consola de desarrollador de Eduzz, abre la pantalla de Seguridad del webhook y genera una clave. Puedes registrar las que quieras, una por destino, lo que ayuda cuando el mismo evento va a sistemas distintos. Eduzz firma cada mensaje con esa clave y envía el resultado en el encabezado x-signature.

  2. 2

    Crea la configuración del webhook

    En el menú Configurações, haz clic en Nova configuração: nombre, clave de acceso, URL de envío de los datos y los eventos deseados. Eduzz recomienda probar la URL, que debe responder de 200 a 299. Hay un campo para limitar el máximo de eventos en paralelo hacia tu URL, útil en un lanzamiento. El webhook envía solo eventos de los productos de la cuenta con la que iniciaste sesión.

  3. 3

    Genera la URL en ZapZap y guarda la clave

    En Fluxos, Recuperar vendas, elige Eduzz, el objetivo y el número, y pega la URL generada (https://api.zapzapapi.com/api/webhooks/flow/{segredo}) en Eduzz. Guarda la clave secreta en el asistente: ZapZap calcula el HMAC-SHA256 del cuerpo en bruto, acepta el resultado en hexadecimal o base64 y rechaza con 401 lo que no coincida.

  4. 4

    Responde al ping y comprueba el teléfono

    Al activar o verificar la URL, Eduzz envía un evento ping, con data.message igual a ping, y espera un 2xx. En ZapZap, el ping no trae teléfono y responde triggered: 0, lo cual es esperado. Después dispara un evento real de prueba y mira, en la muestra del webhook, qué campo se detectó como teléfono (data.buyer.cellphone, data.buyer.phone o data.customer.phone en el carrito).

  5. 5

    Usa los enlaces del payload en el mensaje

    En el editor del flujo, el texto de partida usa {nome} y {produto}. Para incluir el enlace de recuperación o del boleto, el camino es un endpoint tuyo, que lee data.saleRecoveryUrl o data.bankslipUrl y envía por la API de ZapZap, como en el ejemplo de abajo.

Ejemplo

// Valida el x-signature de Eduzz y envía el enlace de recuperación por ZapZap
import crypto from 'node:crypto';
import express from 'express';
const app = express();

app.post('/eduzz', express.raw({ type: 'application/json' }), async (req, res) => {
  const esperado = crypto.createHmac('sha256', process.env.EDUZZ_CHAVE).update(req.body).digest('hex');
  const recebido = String(req.get('x-signature') || '');
  const ok = recebido.length === esperado.length && crypto.timingSafeEqual(Buffer.from(recebido), Buffer.from(esperado));
  if (!ok) return res.sendStatus(401);

  const ev = JSON.parse(req.body.toString('utf8'));
  res.sendStatus(200);
  if (ev.event === 'ping' || (await jaProcessado(ev.id))) return; // tu función: UNIQUE en el id

  const d = ev.data;
  const fone = String(d.buyer?.cellphone || d.buyer?.phone || d.customer?.phone || '').replace(/\D/g, '');
  const link = d.saleRecoveryUrl;
  if (!fone || !link) 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! Tu pedido quedó a medias. Retómalo por aquí: ' + link }),
  });
});

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

Eventos de Eduzz que se vuelven mensaje

La referencia pública de Eduzz lista decenas de eventos. Los de la tabla son los que mejor encajan en cobro y recuperación, con el campo de teléfono que documenta la referencia.

ObjetivoEventoTeléfono y enlaces en el payload
Pago confirmadomyeduzz.invoice_paiddata.buyer.cellphone, phone y phone2; data.paidAt; data.paymentMethod (bankslip, pix, creditCard...)
Factura abiertamyeduzz.invoice_openeddata.buyer.cellphone; data.saleRecoveryUrl; data.bankslipUrl
Factura expiradamyeduzz.invoice_expiredmismo sobre de factura; úsalo para el último recordatorio
Carrito abandonadosun.cart_abandonmentdata.customer.phone; data.saleRecoveryUrl; data.productId
Reembolso y disputamyeduzz.invoice_refunded, myeduzz.invoice_chargebackPara retirar acceso, no para vender

x-signature en la práctica

Según Eduzz, el x-signature es hmac('sha256', chave_secreta, corpo_da_requisição); si el resultado es idéntico al del encabezado, el mensaje es legítimo. La documentación no dice si el resultado viene en hexadecimal o base64, por eso ZapZap acepta ambos. En tu endpoint, calcula sobre el cuerpo crudo (los bytes recibidos), antes de cualquier parse del JSON, y compara en tiempo constante. Recalcular sobre un JSON reserializado cambia espacios y orden de claves y la firma deja de coincidir.

  • Clave: una por destino. Si algún día la clave se filtra, genera otra y apunta la configuración hacia ella.
  • Replay: el sobre tiene sentDate, pero la documentación no dice que entre en la firma. Usa el id del evento para descartar repetidos.
  • Paralelismo: el límite de eventos en paralelo de la configuración protege tu servidor en un lanzamiento. ZapZap acepta 120 solicitudes por minuto por webhook; por encima de eso responde 429.

Cuándo NO usar el asistente

El asistente arma mensajes con nombre y producto. Si quieres poner el saleRecoveryUrl o el bankslipUrl en el texto, o diferenciar el mensaje por data.paymentMethod, usa un endpoint tuyo o n8n. Tampoco uses los eventos de reembolso y chargeback para mensajes de marketing: el contacto por esos eventos es para resolver el problema del cliente. Y no escribas a quien nunca inició una compra; eso es prospección y necesita otra base.

Fuentes y fecha de verificación

Los eventos, los campos de teléfono, saleRecoveryUrl, bankslipUrl, la firma x-signature, la creación de la configuración y el evento ping se verificaron el 29 de octubre de 2026 en el Eduzz Developer Hub (developers.eduzz.com), en las páginas de Webhook (Criando um webhook, Segurança, Evento de Verificação) y en la especificación OpenAPI pública de los webhooks. El comportamiento de ZapZap proviene del código del producto en producción.

Preguntas frecuentes

¿Cómo prueba Eduzz que el webhook es suyo?

Enviando el encabezado x-signature, un HMAC-SHA256 del cuerpo de la solicitud calculado con la clave secreta que registraste. Recalculas sobre el cuerpo crudo y comparas; si es igual, el mensaje es legítimo. ZapZap hace esa verificación cuando guardas la clave en el asistente.

¿Por qué recibo un evento ping sin teléfono?

El ping sirve para verificar la URL: tiene data.message igual a ping y espera un 2xx. No es una venta. En ZapZap responde triggered: 0, lo cual es normal, y en tu endpoint debes ignorarlo.

¿Qué campo tiene el celular del comprador?

En los eventos de factura, la referencia documenta data.buyer.cellphone, data.buyer.phone y data.buyer.phone2. En el carrito abandonado el campo es data.customer.phone. ZapZap prefiere el celular cuando existen ambos, pero revisa en la muestra cuál se detectó.

¿Se puede enviar el enlace de recuperación de la venta?

El payload de la factura abierta y del carrito abandonado trae data.saleRecoveryUrl. Para poner ese enlace en el texto, usa un endpoint tuyo o n8n, que leen el campo y llaman a la API de ZapZap. El texto del asistente es fijo con nombre y producto.

Mi servidor no aguanta el volumen de un lanzamiento. ¿Y ahora?

La configuración del webhook en Eduzz tiene un límite de eventos enviados en paralelo hacia tu URL. Define un valor que tu servidor aguante y deja que la cola interna se encargue del resto. Responde 2xx rápido y envía el WhatsApp en segundo plano.

¿Eduzz envía eventos de otras cuentas?

No. El webhook envía solo eventos de los productos que pertenecen a la cuenta usada en la configuración. Si faltan eventos, revisa que hayas iniciado sesión en la cuenta que tiene los productos.

¿Puedo usar esto para recordarle a quien no paga desde hace semanas?

Con cuidado. El recordatorio de factura expirada debe ser corto, respetuoso y con una forma de dejar de recibir mensajes. Un cobro que expone o avergüenza al deudor es un problema jurídico además de un riesgo de bloqueo, y ZapZap, canal no oficial, no garantiza que el número no será limitado.

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.