Webhooks
Los webhooks permiten actualizar tu ERP sin consultar constantemente. La consulta status/batch sigue siendo la fuente de reconciliacion y puede usarse como respaldo.
Configuracion
Configura el endpoint desde el portal de Zarela, seleccionando el ambiente, una URL HTTPS publica y un secreto generado por tu equipo. La configuracion, pausa, rotacion del secreto y replay manual requieren una sesion autorizada del portal; no uses cookies del portal desde tu ERP.
Requisitos de la URL en produccion:
- HTTPS con certificado valido.
- Accesible desde Internet.
- No puede apuntar a
localhost, metadata services, redes privadas ni redireccionar a otro destino. - Debe responder rapidamente con cualquier codigo
2xxdespues de persistir el evento.
Solicitud recibida
POST /tu-endpoint HTTP/1.1
Content-Type: application/json
User-Agent: Zarela-Webhooks/1.0
Zarela-Event-Id: 550e8400-e29b-41d4-a716-446655440000
Zarela-Event-Type: ecf.queued
Zarela-Signature: t=1783706400,v1=...
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"type": "ecf.queued",
"createdAt": "2026-07-10T14:00:00.000Z",
"tenantId": "tenant-id",
"environment": "ecf",
"document": {
"id": "document-id",
"type": "E31",
"encf": "E310000000010",
"status": "queued_for_signing",
"trackId": null
},
"data": {
"severity": "info",
"message": "Documento encolado",
"payload": {}
}
}
Para documentos inbound, escucha dgii.public.receiver.ecf.received. El identificador requerido para una aprobacion comercial llega en data.payload.inboundDocumentId.
Verificar la firma
Calcula HMAC-SHA256 sobre:
{timestamp}.{rawBody}
Usa el secreto del endpoint y compara el hexadecimal resultante con v1 en Zarela-Signature. Debes usar el body crudo exacto, antes de parsear o volver a serializar JSON. Rechaza timestamps con mas de cinco minutos de diferencia y compara firmas en tiempo constante.
import { createHmac, timingSafeEqual } from "node:crypto";
function verificarWebhook(rawBody, signatureHeader, secret) {
const parts = Object.fromEntries(
signatureHeader.split(",").map((part) => part.split("="))
);
const timestamp = Number(parts.t);
if (!Number.isFinite(timestamp)) return false;
if (Math.abs(Math.floor(Date.now() / 1000) - timestamp) > 300) return false;
const expected = createHmac("sha256", secret)
.update(`${timestamp}.${rawBody}`)
.digest("hex");
if (expected.length !== String(parts.v1 || "").length) return false;
return timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1));
}
Idempotencia y reintentos
- Guarda
Zarela-Event-Idcon restriccion unica antes de aplicar cambios. - Si ya procesaste el ID, responde
200sin repetir efectos. - Zarela considera exitosa cualquier respuesta
2xx. - Errores temporales se reintentan con backoff hasta un maximo operativo de ocho intentos.
- Redirecciones no se siguen.
- Puedes revisar entregas fallidas y solicitar replay desde el portal.
Usa document.status como snapshot del evento. Si un evento llega tarde, duplicado o fuera de orden, consulta status/batch antes de reemplazar un estado final de tu ERP.