Skip to Content
Webhooks

Webhooks

Invece di fare polling, sottoscrivi gli eventi in uscita: Kinmu invia un POST al tuo endpoint HTTPS quando succede qualcosa di rilevante. Ogni consegna è firmata secondo la convenzione Standard Webhooks verifica sempre la firma prima di elaborare l’evento.

La gestione degli endpoint richiede lo scope webhooks:manage e si fa dalla dashboard o tramite gli endpoint /v1/webhook-endpoints.

Sottoscrizione

Crea un endpoint indicando la tua URL (HTTPS) e gli eventi che ti interessano:

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 risposta include il secret di firma (whsec_…), visibile una sola volta. Conservalo: ti serve per verificare le firme.

Gestione disponibile: GET (elenco), GET /{id}, PATCH /{id} (eventi/stato), DELETE /{id}, POST /{id}/test (invia un ping), GET /{id}/deliveries (ultime consegne) e POST /{id}/deliveries/{deliveryId}/retry (riaccoda una consegna specifica). Massimo 5 endpoint attivi per azienda.

Eventi

employee.created, employee.updated, employee.terminated, checkin.created, absence.requested, absence.approved, absence.denied, absence.cancelled, vacation_balance.updated, report.completed, ping.

Payload dell’evento

{ "id": "evt_…", "type": "absence.approved", "created_at": "…", "company_id": "…", "data": { } }

data contiene id pubblici + stato (il minimo); quando ti servono tutti i campi, reidrata la risorsa completa con una GET.

Verifica della firma

Ogni consegna arriva con tre header:

HeaderContenuto
webhook-idId univoco dell’evento (idempotenza nel tuo receiver).
webhook-timestampTimestamp Unix dell’invio.
webhook-signaturev1,<firma> (possono essercene più di una, separate da spazio).

Algoritmo: firma = base64( HMAC_SHA256( key, "{webhook-id}.{webhook-timestamp}.{body_raw}" ) ), con prefisso v1,. La key è la parte dopo whsec_ del secret (mostrato una sola volta alla creazione del webhook), decodificata da base64 a byte. La firma si calcola sul body raw (i byte esatti ricevuti), non sul JSON ri-serializzato.

Verifica (rifiuta se non coincide o se webhook-timestamp differisce di oltre 5 min dal tuo orologio):

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 e firma sul body raw (i byte esatti ricevuti), non sul JSON ri-serializzato — qualsiasi riformattazione invalida la firma. Usa il webhook-id come chiave di idempotenza nel tuo receiver per scartare i reinvii.

Retry

In caso di errore (o risposta ≠ 2xx) Kinmu riprova con backoff: 1 m, 5 m, 30 m, 2 h, 12 h, 24 h. Rispondi 2xx in fretta; se l’elaborazione è lenta, falla in async. Un endpoint che fallisce per 7 giorni di fila si disattiva automaticamente.

Puoi riaccodare manualmente una consegna fallita con POST /v1/webhook-endpoints/{id}/deliveries/{deliveryId}/retry (usa l’id della consegna che vedi in GET /{id}/deliveries).

Best practice

Rispondi in fretta, elabora in background

Verifica la firma, accoda il lavoro e rispondi 2xx subito.

Verifica sempre la firma

Non elaborare nessun payload senza verificare webhook-signature. Tratta il secret come una credenziale.

Sii idempotente

Deduplica per webhook-id. I retry sono normali.

Testa con ping

Usa POST /v1/webhook-endpoints/{id}/test per ricevere un evento ping e validare la tua integrazione end-to-end.

Last updated on