Webhooks
En lugar de hacer polling a GET /api/v1/invoices/{id}/status, registra una URL y CUCU te notifica en tiempo real cada vez que ocurre un evento relevante: una factura se valida o rechaza, se abre o cierra una contingencia, o cambia el estado de la conexión con el SIAT.
Registrar un endpoint
POST /api/v1/webhooks/endpoints
| Parametro | Ubicacion | Tipo | Req | Descripcion |
|---|
X-API-Key | Header | string | Si | Tu API Key |
url | Body | string | Si | URL https:// que recibirá las entregas |
events | Body | array<string> | Si | Eventos a suscribir. Acepta *, namespace.* (ej. contingency.*) o el tipo exacto |
description | Body | string | No | Nota interna para identificar el endpoint |
Respuesta (201):
El secret solo se muestra completo en este momento (y al rotarlo). Guárdalo de forma segura — lo necesitas para verificar la firma de cada entrega.
Gestionar endpoints
| Accion | Endpoint |
|---|
| Listar endpoints | GET /api/v1/webhooks/endpoints |
| Ver un endpoint | GET /api/v1/webhooks/endpoints/{id} |
| Actualizar (url, events, activo) | PUT /api/v1/webhooks/endpoints/{id} |
| Eliminar | DELETE /api/v1/webhooks/endpoints/{id} |
| Rotar secreto | POST /api/v1/webhooks/endpoints/{id}/rotate-secret |
Todas requieren el header X-API-Key.
Cada tenant puede registrar un máximo de 20 endpoints. Si necesitas más, contacta a soporte.
Catálogo de eventos
| Evento | Descripción |
|---|
invoice.* | Un evento por cada cambio de estado de una factura (validada, rechazada, anulada), reflejando los mismos estados documentados en el ciclo de vida de la factura |
contingency.opened | Se activó contingencia offline en un punto de venta |
contingency.closed | La contingencia finalizó y las facturas se sincronizaron con el SIAT |
contingency.package_rejected | El SIAT rechazó el paquete de facturas enviado al reconectar |
siat.connection_lost | CUCU detectó que el SIAT dejó de responder |
siat.connection_restored | La conexión con el SIAT se restableció |
siat.token_expiring | El token de sesión SIAT está por expirar |
certificate.expiring | El certificado digital del tenant está por vencer |
cufd.renewal_failed | Falló la renovación automática del CUFD |
cuis.renewal_failed | Falló la renovación automática del CUIS |
Al registrar un endpoint puedes suscribirte a * (todos los eventos), a un namespace completo (contingency.*, siat.*) o a un tipo exacto. La suscripción se valida contra este mismo catálogo, así que nunca puedes suscribirte a un evento inexistente.
Seguridad
- HTTPS obligatorio. Solo se aceptan URLs
https://.
- Anti-SSRF. Al registrar o rotar un endpoint, CUCU resuelve el host y rechaza direcciones en rangos privados o de loopback.
- Firma en cada entrega. Todas las entregas se firman con HMAC-SHA256 siguiendo el estándar Standard Webhooks, para que la verificación sea sencilla y predecible en cualquier lenguaje.
| Header | Descripción |
|---|
webhook-id | UUID único de esta entrega. Úsalo para deduplicar en tu receptor. |
webhook-timestamp | UNIX timestamp del momento del envío. |
webhook-signature | Firma de autenticidad. Formato: v1,<base64(HMAC-SHA256)>. |
webhook-event | Tipo de evento entregado (ej. invoice.validated). |
Verificación de firma
Compara firma_esperada con el valor de webhook-signature (después del prefijo v1,) usando una comparación de tiempo constante.
Payload de ejemplo
Política de reintentos
Tu endpoint debe responder 2xx dentro del timeout configurado para que la entrega se considere exitosa. Si no responde o responde con error, CUCU reintenta con backoff exponencial hasta 8 intentos. Un dispatcher interno procesa la cola de entregas pendientes cada 10 segundos.
Diseña tu receptor para ser idempotente usando webhook-id — el mismo evento puede llegar más de una vez ante un reintento de red.
Auditoría y reenvío manual
| Accion | Endpoint |
|---|
| Listar entregas de un endpoint | GET /api/v1/webhooks/endpoints/{id}/deliveries |
| Forzar el reenvío de una entrega | POST /api/v1/webhooks/deliveries/{deliveryId}/redeliver |
Útil para debugging: revisa el historial de intentos, el código de respuesta recibido y fuerza un reenvío manual sin esperar al backoff automático.