Webhooks
En lugar de hacer polling, suscríbete a eventos salientes: Kinmu envía un POST a tu endpoint HTTPS cuando ocurre algo relevante. Cada entrega va firmada con la convención Standard Webhooks — verifica siempre la firma antes de procesar el evento.
La gestión de endpoints requiere el scope webhooks:manage y se hace desde el dashboard o vía los endpoints /v1/webhook-endpoints.
Suscripción
Crea un endpoint indicando tu URL (HTTPS) y los eventos que te interesan:
curl -s -X POST "https://api.kinmu.app/v1/webhook-endpoints" \
-H "Authorization: Bearer kinmu_sk_live_…" \
-H "Content-Type: application/json" \
-d '{
"url": "https://tu-app.example.com/webhooks/kinmu",
"events": ["absence.approved", "checkin.created"],
"description": "Sync con nuestro ERP"
}'La respuesta incluye el secreto de firma (whsec_…), visible una sola vez. Guárdalo: lo necesitas para verificar las firmas.
Gestión disponible: GET (lista), GET /{id}, PATCH /{id} (eventos/estado), DELETE /{id}, POST /{id}/test (envía un ping), GET /{id}/deliveries (últimas entregas) y POST /{id}/deliveries/{deliveryId}/retry (reencola una entrega concreta). Máximo 5 endpoints activos por empresa.
Eventos
employee.created, employee.updated, employee.terminated, checkin.created, absence.requested, absence.approved, absence.denied, absence.cancelled, vacation_balance.updated, report.completed, ping.
Payload del evento
{ "id": "evt_…", "type": "absence.approved", "created_at": "…", "company_id": "…", "data": { … } }data lleva ids públicos + estado (mínimo); rehidrata vía GET el recurso completo cuando necesites todos los campos.
Verificación de la firma
Cada entrega llega con tres headers:
| Header | Contenido |
|---|---|
webhook-id | Id único del evento (idempotencia en tu receptor). |
webhook-timestamp | Timestamp Unix del envío. |
webhook-signature | v1,<firma> (puede haber varias separadas por espacio). |
Algoritmo: firma = base64( HMAC_SHA256( key, "{webhook-id}.{webhook-timestamp}.{body_crudo}" ) ), prefijada con v1,.
La key es la parte tras whsec_ del secreto (mostrado una sola vez al crear el webhook), decodificada de base64 a bytes. Firma sobre el cuerpo crudo (bytes exactos recibidos), no sobre el JSON re-serializado.
Verificación (rechaza si no coincide o si webhook-timestamp difiere > 5 min de tu reloj):
Node.js
const crypto = require('crypto');
function verify(secret, headers, rawBody) {
const id = headers['webhook-id'];
const ts = headers['webhook-timestamp'];
if (Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false; // ±5 min
const key = Buffer.from(secret.replace(/^whsec_/, ''), 'base64');
const expected = 'v1,' + crypto
.createHmac('sha256', key)
.update(`${id}.${ts}.${rawBody}`)
.digest('base64');
return headers['webhook-signature']
.split(' ')
.some(sig => crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected)));
}Verifica y firma sobre el cuerpo crudo (bytes exactos recibidos), no sobre el JSON re-serializado — cualquier reformateo invalida la firma. Usa el webhook-id como clave de idempotencia en tu receptor para descartar reenvíos.
Reintentos
Ante fallo (o respuesta ≠ 2xx) Kinmu reintenta con backoff: 1 m, 5 m, 30 m, 2 h, 12 h, 24 h. Responde 2xx rápido; procesa async si tardas. Un endpoint que falla 7 días seguidos se auto-desactiva.
Puedes reencolar manualmente una entrega fallida con POST /v1/webhook-endpoints/{id}/deliveries/{deliveryId}/retry (usa el id de la entrega que ves en GET /{id}/deliveries).
Buenas prácticas
Responde rápido, procesa en background
Verifica la firma, encola el trabajo y responde 2xx de inmediato.
Verifica siempre la firma
No proceses ningún payload sin verificar webhook-signature. Trata el secreto como una credencial.
Sé idempotente
Deduplica por webhook-id. Los reintentos son normales.
Prueba con ping
Usa POST /v1/webhook-endpoints/{id}/test para recibir un evento ping y validar tu integración de punta a punta.