Webhooks
Statt zu pollen, abonniere ausgehende Events: Kinmu sendet einen POST an deinen HTTPS-Endpoint, sobald etwas Relevantes passiert. Jede Zustellung ist nach der Konvention Standard Webhooks signiert — verifiziere die Signatur immer, bevor du das Event verarbeitest.
Die Verwaltung von Endpoints erfordert den Scope webhooks:manage und erfolgt über das Dashboard oder die Endpoints unter /v1/webhook-endpoints.
Endpoint registrieren
Lege einen Endpoint an, mit deiner URL (HTTPS) und den Events, die dich interessieren:
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"
}'Die Antwort enthält das Signing-Secret (whsec_…), nur ein einziges Mal sichtbar. Bewahre es auf: Du brauchst es zum Verifizieren der Signaturen.
Verfügbare Verwaltung: GET (Liste), GET /{id}, PATCH /{id} (Events/Status), DELETE /{id}, POST /{id}/test (sendet ein ping), GET /{id}/deliveries (letzte Zustellungen) und POST /{id}/deliveries/{deliveryId}/retry (stellt eine bestimmte Zustellung neu ein). Maximal 5 aktive Endpoints pro Unternehmen.
Events
employee.created, employee.updated, employee.terminated, checkin.created, absence.requested, absence.approved, absence.denied, absence.cancelled, vacation_balance.updated, report.completed, ping.
Event-Payload
{ "id": "evt_…", "type": "absence.approved", "created_at": "…", "company_id": "…", "data": { … } }data enthält öffentliche IDs + Status (minimal); lade die vollständige Ressource per GET nach, wenn du alle Felder brauchst.
Signatur verifizieren
Jede Zustellung kommt mit drei Headern:
| Header | Inhalt |
|---|---|
webhook-id | Eindeutige ID des Events (Idempotenz in deinem Receiver). |
webhook-timestamp | Unix-Timestamp des Versands. |
webhook-signature | v1,<firma> (es können mehrere sein, durch Leerzeichen getrennt). |
Algorithmus: firma = base64( HMAC_SHA256( key, "{webhook-id}.{webhook-timestamp}.{body_crudo}" ) ), mit dem Präfix v1,.
Der Key ist der Teil nach whsec_ des Secrets (nur einmal beim Anlegen des Webhooks angezeigt), von Base64 zu Bytes dekodiert. Signiert wird der rohe Body (exakt empfangene Bytes), nicht das re-serialisierte JSON.
Verifizierung (lehne ab, wenn die Signatur nicht übereinstimmt oder webhook-timestamp mehr als 5 Min. von deiner Uhr abweicht):
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)));
}Verifiziere und signiere über den rohen Body (exakt empfangene Bytes), nicht über das re-serialisierte JSON — jede Umformatierung macht die Signatur ungültig. Verwende die webhook-id als Idempotenzschlüssel in deinem Receiver, um erneute Zustellungen zu verwerfen.
Retries
Bei Fehlern (oder einer Antwort ≠ 2xx) wiederholt Kinmu mit Backoff: 1 min, 5 min, 30 min, 2 h, 12 h, 24 h. Antworte schnell mit 2xx; verarbeite asynchron, wenn du länger brauchst. Ein Endpoint, der 7 Tage in Folge fehlschlägt, wird automatisch deaktiviert.
Eine fehlgeschlagene Zustellung kannst du manuell neu einstellen mit POST /v1/webhook-endpoints/{id}/deliveries/{deliveryId}/retry (verwende die id der Zustellung aus GET /{id}/deliveries).
Best Practices
Antworte schnell, verarbeite im Hintergrund
Verifiziere die Signatur, stelle die Arbeit in eine Queue und antworte sofort mit 2xx.
Verifiziere immer die Signatur
Verarbeite keinen Payload, ohne webhook-signature geprüft zu haben. Behandle das Secret wie ein Credential.
Sei idempotent
Dedupliziere über die webhook-id. Retries sind normal.
Teste mit ping
Nutze POST /v1/webhook-endpoints/{id}/test, um ein ping-Event zu erhalten und deine Integration Ende-zu-Ende zu validieren.