Saltar al contenido principal

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 2xx despues 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-Id con restriccion unica antes de aplicar cambios.
  • Si ya procesaste el ID, responde 200 sin 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.
Confirma el estado

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.