Ir al contenido

Webhooks

Los webhooks permiten que tu sistema reciba notificaciones HTTP automáticas cuando ocurren eventos importantes en Redcumbre. En lugar de consultar periódicamente nuestra API (polling), configurás una URL y nosotros te avisamos cuando algo sucede.


Tu Sistema Redcumbre Evento
│ │ │
│── Configura webhook URL ─────▶│ │
│ (POST /config/webhooks) │ │
│◀── Secret HMAC ──────────────│ │
│ │ │
│ │◀─────── Ocurre evento ─────│
│ │ (BHE emitida, etc) │
│ │ │
│◀── POST webhook ─────────────│ │
│ (JSON + firma HMAC) │ │
│ │ │
│── 200 OK ────────────────────▶│ │
│ │── Log: WEBHOOK_SENT │

Características:

  • Procesamiento asíncrono (no bloquea la operación principal)
  • Reintentos automáticos con backoff exponencial
  • Firma HMAC-SHA256 para validar autenticidad
  • Headers personalizados para autenticación

La configuración de webhooks se realiza desde el panel de administración de Redcumbre:

Administración → Configuración → Webhooks

CampoDescripción
URL del WebhookURL HTTPS donde recibirás las notificaciones
Headers PersonalizadosHeaders HTTP adicionales para autenticación (ej: Authorization: Bearer token)
Eventos HabilitadosSelecciona qué eventos quieres recibir
ActivoHabilitar/deshabilitar el envío de webhooks

Al crear la configuración, el sistema genera automáticamente un secret de 64 caracteres hexadecimales. Este secret se usa para firmar los webhooks con HMAC-SHA256.


EventoDescripción
tokenizacion.completadaEmisor autorizado exitosamente vía SII
emisor.revocadoEmisor revocado (DTE o Tributario)
bhe.emitidaBoleta de Honorarios emitida al SII
bhe.emision_terminadaPDF SII disponible (modos SINC_PARCIAL/ASINCRONO)
bhe.pdf_sii_fallidoPDF SII falló después de 7 días de reintentos
bhe.emision_fallidaEmisión asíncrona falló después de 7 horas
EventoDescripción
onexo.postulacion-completadaPostulante completó el flujo de entrevista
onexo.postulacion-aprobadaRevisor aprobó la postulación
onexo.postulacion-rechazadaRevisor rechazó la postulación
onexo.aclaracion-solicitadaRevisor solicitó aclaraciones adicionales
onexo.curso-aprobadoCurso completado y aprobado
EventoDescripción
validafirma.documento-completadoDocumento firmado exitosamente

Se envía cuando un usuario completa el flujo de autorización de credenciales SII.

{
"event": "tokenizacion.completada",
"tenantId": "8",
"sessionId": "c6c53392-bdb0-4053-8b9d-5b34296d63a3",
"code": "83aK0esV0V21u8-krvuMiXx1OqheSVy56E6tBG3OjX8",
"emisorId": "cmikf6ei00001sek0mxzcf668",
"rut": "77438768-4",
"tipoAutenticacion": "CLAVE_TRIBUTARIA",
"tipoEmisor": "tributario",
"correlationId": "mi-referencia-123",
"lookupData": {
"razonSocial": "EMPRESA EJEMPLO SPA",
"giro": "SERVICIOS INFORMATICOS",
"direcciones": [
{
"ciudad": "SANTIAGO",
"comuna": "PROVIDENCIA",
"direccion": "AV. PROVIDENCIA 1234"
}
]
},
"timestamp": "2025-12-10T15:30:00Z"
}

Se envía cuando un emisor es revocado (por el usuario o administrador).

{
"event": "emisor.revocado",
"data": {
"emisorId": "cmikf6ei00001sek0mxzcf668",
"emisorType": "tributario",
"rut": "77438768-4",
"razonSocial": "EMPRESA EJEMPLO SPA",
"tenantId": "8",
"revokedBy": "USER",
"motivoRevocacion": null
},
"timestamp": "2025-12-10T15:30:00Z"
}
CampoDescripción
emisorTypetributario o dte
revokedByUSER (usuario final) o ADMIN (administrador)
motivoRevocacionMotivo de revocación (solo cuando revokedBy: ADMIN)

Se envía inmediatamente después de emitir una BHE exitosamente.

{
"event": "bhe.emitida",
"tenantId": "8",
"bheId": "cm5abc123",
"folioSii": "12345678",
"emisor": {
"rut": "78012039-8",
"razonSocial": "JUAN PÉREZ GONZÁLEZ"
},
"destinatario": {
"rut": "12345678-9",
"nombre": "EMPRESA CLIENTE SA",
"sinDestinatario": false
},
"montos": {
"bruto": 225000,
"ppm": 30938,
"liquido": 194062
},
"tipoRetencion": "RETRECEPTOR",
"canalEmision": "API_SYNC",
"correlationId": "mi-referencia-123",
"sandbox": false,
"timestamp": "2025-12-10T15:30:00Z"
}

Se envía cuando el PDF SII está disponible (modos SINC_PARCIAL y ASINCRONO).

{
"event": "bhe.emision_terminada",
"tenantId": "8",
"bheId": "cm5abc123",
"folioSii": "12345678",
"pdfInternoUrl": "https://storage.googleapis.com/.../interno.pdf",
"pdfSiiUrl": "https://storage.googleapis.com/.../sii.pdf",
"estadoPdfSii": "DISPONIBLE",
"timestamp": "2025-12-10T15:35:00Z"
}

Se envía después de 7 días de reintentos fallidos para descargar el PDF SII.

{
"event": "bhe.pdf_sii_fallido",
"tenantId": "8",
"bheId": "cm5abc123",
"folioSii": "12345678",
"intentosRealizados": 30,
"primerIntento": "2025-12-01T15:30:00Z",
"ultimoIntento": "2025-12-08T15:30:00Z",
"ultimoError": "SII_PDF_NO_DISPONIBLE",
"timestamp": "2025-12-08T15:30:00Z"
}

Se envía cuando una emisión asíncrona falla después de ~7 horas de reintentos.

{
"event": "bhe.emision_fallida",
"tenantId": "8",
"correlationId": "mi-referencia-123",
"intentosRealizados": 15,
"primerIntento": "2025-12-08T10:00:00Z",
"ultimoIntento": "2025-12-08T17:00:00Z",
"ultimoError": "SII_SERVICE_UNAVAILABLE",
"timestamp": "2025-12-08T17:00:00Z"
}

Se envía cuando un postulante completa el flujo de entrevista virtual.

{
"event": "onexo.postulacion-completada",
"tenantId": "8",
"postulacionId": "cm5xyz789",
"procesoId": "cm5proceso123",
"procesoTitulo": "Captador de Aportes RM",
"persona": {
"rut": "12345678-9",
"nombreCompleto": "Juan Pérez González",
"email": "juan.perez@email.com"
},
"timestamp": "2025-12-10T15:30:00Z"
}

Se envía cuando un revisor aprueba una postulación.

{
"event": "onexo.postulacion-aprobada",
"tenantId": "8",
"postulacionId": "cm5xyz789",
"procesoId": "cm5proceso123",
"procesoTitulo": "Captador de Aportes RM",
"persona": {
"rut": "12345678-9",
"nombreCompleto": "Juan Pérez González",
"email": "juan.perez@email.com"
},
"revisor": {
"userId": "zitadel-user-123",
"nombre": "María García"
},
"timestamp": "2025-12-10T16:00:00Z"
}

Se envía cuando un revisor rechaza una postulación.

{
"event": "onexo.postulacion-rechazada",
"tenantId": "8",
"postulacionId": "cm5xyz789",
"procesoId": "cm5proceso123",
"procesoTitulo": "Captador de Aportes RM",
"persona": {
"rut": "12345678-9",
"nombreCompleto": "Juan Pérez González",
"email": "juan.perez@email.com"
},
"revisor": {
"userId": "zitadel-user-123",
"nombre": "María García"
},
"motivoRechazo": "Documentación incompleta",
"timestamp": "2025-12-10T16:00:00Z"
}

Se envía cuando un revisor solicita aclaraciones adicionales.

{
"event": "onexo.aclaracion-solicitada",
"tenantId": "8",
"postulacionId": "cm5xyz789",
"procesoId": "cm5proceso123",
"procesoTitulo": "Captador de Aportes RM",
"persona": {
"rut": "12345678-9",
"nombreCompleto": "Juan Pérez González",
"email": "juan.perez@email.com"
},
"revisor": {
"userId": "zitadel-user-123",
"nombre": "María García"
},
"mensaje": "Por favor adjunte copia de su certificado de antecedentes",
"timestamp": "2025-12-10T16:00:00Z"
}

Se envía cuando un alumno completa y aprueba un curso.

{
"event": "onexo.curso-aprobado",
"tenantId": "8",
"cursoId": "cm5curso123",
"cursoTitulo": "Inducción Básica",
"alumno": {
"rut": "12345678-9",
"nombreCompleto": "Juan Pérez González",
"email": "juan.perez@email.com"
},
"puntaje": 85,
"aprobado": true,
"timestamp": "2025-12-10T18:00:00Z"
}

Se envía cuando un documento es firmado exitosamente en ValidaFirma.

{
"event": "validafirma.documento-completado",
"tenantId": "8",
"documentoId": "vf-doc-123",
"procesoId": "cm5proceso123",
"postulacionId": "cm5xyz789",
"firmantes": [
{
"rut": "12345678-9",
"nombre": "Juan Pérez González",
"email": "juan.perez@email.com",
"firmadoEn": "2025-12-10T15:30:00Z"
}
],
"documentoUrl": "https://storage.googleapis.com/.../contrato-firmado.pdf",
"timestamp": "2025-12-10T15:30:00Z"
}

Cada webhook incluye una firma HMAC-SHA256 en el header X-Webhook-Signature que debes validar para asegurar que el webhook es auténtico.

const crypto = require('crypto');
function validateWebhookSignature(payload, signature, secret) {
// Calcular HMAC del payload
const hmac = crypto.createHmac('sha256', secret);
hmac.update(JSON.stringify(payload));
const expectedSignature = hmac.digest('hex');
// Comparar con signature recibida (timing-safe)
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expectedSignature)
);
}
// Uso en endpoint receptor
app.post('/webhooks/redcumbre', express.json(), (req, res) => {
const signature = req.headers['x-webhook-signature'];
const secret = process.env.REDCUMBRE_WEBHOOK_SECRET;
if (!validateWebhookSignature(req.body, signature, secret)) {
return res.status(401).json({ error: 'Invalid signature' });
}
// Procesar webhook...
const { event, ...data } = req.body;
console.log(`Received ${event}:`, data);
res.status(200).json({ success: true });
});

El sistema reintenta automáticamente con backoff exponencial:

IntentoDelayAcumulado
1Inmediato0s
2+1 segundo1s
3+2 segundos3s
4+4 segundos7s
5+8 segundos15s
6 (final)+16 segundos31s

Total: ~31 segundos desde el primer intento hasta el último.


Código HTTPResultado
200-299Webhook recibido correctamente
4xxError cliente - Se reintentará
5xxError servidor - Se reintentará
TimeoutSin respuesta en 30s - Se reintentará

  1. Responde rápido (< 5 segundos)

    • Timeout máximo: 30 segundos
    • Procesa en background si necesitas más tiempo
  2. Retorna HTTP 200-299 siempre que recibas el webhook

    { "success": true, "receivedAt": "2025-12-10T15:30:00Z" }
  3. Implementa idempotencia

    • Los reintentos pueden enviar el mismo webhook múltiples veces
    • Usa timestamp o un campo único para detectar duplicados
  4. Valida la firma HMAC

    • Nunca proceses webhooks sin validar la firma
    • Usa comparación timing-safe
  5. Logea todos los webhooks recibidos

    • Ayuda al debugging
    • Permite detectar problemas de entrega

webhook.site es una herramienta gratuita para testear webhooks:

  1. Ve a https://webhook.site
  2. Copia tu URL única (ej: https://webhook.site/abc-123)
  3. Configura esa URL en tu tenant
  4. Realiza una operación que genere webhook (ej: emitir BHE)
  5. Verifica en webhook.site que recibiste el POST

Para testear la lógica de reintentos, puedes usar URLs que retornan errores:

Ventana de terminal
# Retorna siempre HTTP 500
https://httpstat.us/500
# Retorna HTTP 503
https://httpstat.us/503
# Delay de 35 segundos (causa timeout)
https://httpbin.org/delay/35

En modo sandbox, los webhooks funcionan normalmente pero los datos son de prueba:

  • API Keys con isSandbox: true envían webhooks con datos simulados
  • No hay llamadas reales al SII
  • Útil para validar tu integración antes de producción

RUTs de prueba:

RUTResultado
78012039-8Empresa completa (FIRERAISE SPA)
77425402-1Empresa con múltiples direcciones
99999999-9Simula error

Para detalles técnicos completos y especificaciones de todos los endpoints:

👉 Ver endpoints de Webhooks en Swagger