Webhooks: eventos, firmas y reintentos
Los webhooks envían un POST JSON a una URL HTTPS de tu sistema cuando se emite un evento suscrito. Configúralos en Desarrolladores, pestaña Webhooks.
Crear una suscripción
Indica la URL HTTPS, selecciona eventos y guarda el secreto que se muestra al crearla. Conserva ese secreto en tu servidor. En Entregas puedes consultar estado, respuesta HTTP e intentos y reintentar los casos fallidos.
Eventos y cobertura actual
| Evento | Flujo que lo emite |
|---|---|
| message.received | Recepción implementada en WhatsApp, Messenger, Instagram y Telegram. |
| message.sent | Respuesta automática implementada en esos canales. |
| conversation.handed_off | Traspaso humano del flujo de WhatsApp. |
| order.created | Recepción de eventos de pedidos de Shopify. |
| order.paid | Confirmación de pago mediante eventos de Shopify. |
| appointment.scheduled | Flujo de creación de citas. |
| appointment.cancelled | Flujo de cancelación de citas. |
La existencia del nombre del evento no significa que todas las acciones del panel, todos los canales o todas las tiendas lo emitan. No uses order.created para dar por cubierta una creación manual de pedido.
Sobre del evento
{
"event": "order.paid",
"data": {
"order_id": "identificador-del-pedido"
},
"timestamp": "2026-09-27T15:00:00.000Z",
"request_id": "identificador-de-la-entrega"
}
El ejemplo ilustra la estructura; data varía según el evento y el flujo emisor. Conserva el cuerpo que recibes y admite campos adicionales.
Encabezados
| Encabezado | Uso |
|---|---|
| X-Synvaxis-Signature | sha256= seguido del HMAC-SHA256 del cuerpo. |
| X-Synvaxis-Event | Nombre del evento. |
| X-Synvaxis-Request-Id | Identificador incluido en el sobre. |
| X-Synvaxis-Delivery-Id | Identificador del registro de entrega. |
| X-Synvaxis-Attempt | Número de intento. |
Verifica el cuerpo original
Calcula la firma sobre los bytes originales, antes de parsear o volver a serializar el JSON. El siguiente receptor de Express muestra la verificación completa. Instala express y configura SYNVAXIS_WEBHOOK_SECRET en el entorno.
import express from 'express';
import crypto from 'node:crypto';
const app = express();
const secret = process.env.SYNVAXIS_WEBHOOK_SECRET;
if (!secret) throw new Error('Falta SYNVAXIS_WEBHOOK_SECRET');
app.post('/hooks/synvaxis', express.raw({ type: 'application/json' }), (req, res) => {
const signature = req.get('X-Synvaxis-Signature') || '';
if (!/^sha256=[a-f0-9]{64}$/.test(signature) || !Buffer.isBuffer(req.body)) {
return res.sendStatus(401);
}
const expected = crypto.createHmac('sha256', secret).update(req.body).digest();
const received = Buffer.from(signature.slice(7), 'hex');
if (!crypto.timingSafeEqual(expected, received)) return res.sendStatus(401);
try {
const event = JSON.parse(req.body.toString('utf8'));
console.log({ event: event.event, requestId: event.request_id });
return res.sendStatus(204);
} catch {
return res.sendStatus(400);
}
});
app.listen(3000);
Es un receptor de verificación: solo registra el tipo y el identificador. Para procesar operaciones reales, guarda primero el evento en una cola duradera con una clave única basada en request_id y responde 2xx cuando quede aceptado. No registres el cuerpo completo si contiene datos de clientes.
Registra esta ruta antes de un middleware global express.json(). Si ese middleware consume el cuerpo primero, perderás los bytes necesarios para verificarlo.
Reintentos
El tiempo máximo por intento es 8 segundos. Una respuesta fuera de 2xx o un fallo de conexión registra un error. Hay hasta 5 intentos automáticos en total: las esperas entre intentos son 30 segundos, 2 minutos, 10 minutos y 1 hora. Son esperas mínimas; la ejecución depende del proceso de reintentos.
Tras el quinto fallo no se programa otro reintento automático. El endpoint deja de recibir nuevas entregas al acumular 20 fallos consecutivos. Revisa Entregas, corrige el receptor y reintenta un fallo; una entrega exitosa restablece el contador. La interfaz no ofrece editar la URL: si necesitas cambiarla, crea otra suscripción y retira la anterior.
Evita procesar dos veces
Los reintentos conservan request_id. Deduplica en almacenamiento persistente antes de aplicar cobros, crear registros o enviar mensajes. No presupongas un orden global ni entrega exactamente una vez. Usa las consultas REST para reconciliar estados cuando sea necesario.