Skip to Content
Webhooks

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:

HeaderContenido
webhook-idId único del evento (idempotencia en tu receptor).
webhook-timestampTimestamp Unix del envío.
webhook-signaturev1,<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):

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.

Last updated on