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:
| Header | Contenuto |
|---|---|
webhook-id | Id univoco dell’evento (idempotenza nel tuo receiver). |
webhook-timestamp | Timestamp Unix dell’invio. |
webhook-signature | v1,<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):
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 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.