3.6 KiB
3.6 KiB
Webhooks salientes
Notificación de eventos de la clínica hacia sistemas externos, con patrón outbox + cron + reintentos + bitácora. Sin webhooks activos suscritos no se crea ninguna fila ni se envía nada (cero impacto).
Modelos
skeen.webhook
| Campo | Descripción |
|---|---|
name, url |
Nombre y URL destino |
eventos |
Claves separadas por coma; vacío = todos |
activo |
Habilitado |
secreto |
Con este secreto se firma cada entrega |
headers |
Cabeceras extra (JSON, opcional) |
notas, creado_por_login |
Metadatos |
ultima_entrega_at |
Última entrega correcta |
fallos_consecutivos |
Se desactiva tras N fallos (default 20, configurable en skeen.webhooks.fallos_max) |
skeen.webhook.evento (outbox)
webhook_id, evento, payload (JSON), estado
(pendiente|enviado|fallido|descartado), intentos, proximo_intento,
respuesta_codigo, respuesta_texto, enviado_at, clave_unica
(idempotencia por evento+objeto).
Catálogo de eventos
| Evento | Cuándo se emite |
|---|---|
cita.creada |
Al crear una cita |
cita.confirmada |
state → confirmed |
cita.cancelada |
state → cancelled |
cita.no_show |
state → no_show |
cita.completada |
state → done |
venta.pagada |
state → paid |
venta.cancelada |
state → cancelled o refunded |
pago.registrado |
Al crear un skeen.pago |
paciente.creado |
Al crear un res.partner con is_patient |
membresia.renovada |
action_renovar |
formulario.llenado |
estado → llenado |
compra.recibida |
estado → recibida/recibida_parcial |
Ejemplo de payload (cita.creada)
{
"id": 123,
"reference": "CITA-0123",
"state": "pending",
"date": "2026-10-20",
"patient_id": 55,
"patient": "María López",
"service_id": 7,
"servicio": "Limpieza facial",
"doctor": "Dra. Pérez"
}
Entrega
Cron cada 5 minutos (skeen.webhook.service._cron_tick) que toma los pendientes
vencidos y hace POST JSON con:
| Cabecera | Valor |
|---|---|
X-Skeen-Event |
Clave del evento |
X-Skeen-Delivery |
UUID de la entrega |
X-Skeen-Signature |
sha256=<HMAC-SHA256 del cuerpo con el secreto> |
- Timeouts cortos (5 s conexión / 15 s lectura).
- Reintentos con backoff: 1, 5, 30 min, 2 h, 6 h (tope 5 intentos); al
agotarlos el evento queda
fallido. - Tras N fallos consecutivos (default 20) el webhook se desactiva.
Verificar la firma (Python)
import hashlib, hmac
firma = request.headers['X-Skeen-Signature'] # 'sha256=...'
esperada = 'sha256=' + hmac.new(
SECRETO.encode(), request.body, hashlib.sha256).hexdigest()
assert hmac.compare_digest(firma, esperada)
Verificar la firma (Node.js)
const crypto = require('crypto');
const esperada = 'sha256=' + crypto
.createHmac('sha256', SECRETO)
.update(req.rawBody)
.digest('hex');
Endpoints
| Método | Ruta | Acceso |
|---|---|---|
| GET/POST | /skeen/frontend/v1/webhooks |
permiso webhooks.editar (admin/gerencia) |
| PUT/DELETE | /skeen/frontend/v1/webhooks/<id> |
idem |
| GET | /skeen/frontend/v1/webhooks/<id>/eventos |
idem (filtra por estado, evento, limit) |
| POST | /skeen/frontend/v1/webhooks/<id>/probar |
idem (evento de prueba) |
| POST | /skeen/frontend/v1/webhook_eventos/<id>/reintentar |
idem |
UI
Configuración → pestaña Webhooks (admin/gerencia): CRUD, selector de eventos, secreto (generado o propio), visor del payload de ejemplo, log de entregas con estado/código/fecha, reintento manual y "probar".