Skip to Content
Conventies

API-conventies

Regels die voor alle resources van v1 gelden.

OnderwerpRegel
FormaatJSON UTF-8. Requests met een body sturen Content-Type: application/json mee.
IDsPublieke UUID’s. Interne numerieke IDs worden nooit blootgesteld.
DatumsISO 8601. Tijdstippen in UTC (2026-07-08T09:30:00Z); kalenderdatums als YYYY-MM-DD.
Base URLVersie in het pad: https://api.kinmu.app/v1 · Dev: https://api.dev.kinmu.app/v1.

Paginering (cursor)

Alle lijst-endpoints pagineren met een cursor:

  • ?limit= (default 25, max 100) · ?cursor=<opaak>.
  • Response: { "data": [...], "meta": { "next_cursor": "…"|null, "has_more": true|false } }.
  • Stuur voor de volgende pagina cursor=<meta.next_cursor> mee. has_more=false of next_cursor=null → einde.
curl "https://api.kinmu.app/v1/employees?limit=50&cursor=eyJpZCI6MTIzfQ" \ -H "Authorization: Bearer kinmu_sk_live_…"

De cursor is opaak: bouw of parse hem niet zelf, maar stuur hem ongewijzigd terug.

Incrementele synchronisatie (updated_since)

De parameter ?updated_since=<ISO 8601> haalt alleen op wat er sinds dat moment is gewijzigd (incrementeel pollen voor BI en ERP’s). Hij geldt niet overal: hij filtert alleen op deze lijst-endpoints.

Lijst-endpointFiltert op updated_since?
GET /v1/employeesJa
GET /v1/check-insJa
GET /v1/absencesJa
GET /v1/locationsJa
GET /v1/unitsJa
GET /v1/webhook-endpointsJa
GET /v1/vacation-balancesNee — wordt geaccepteerd maar genegeerd; verouderd, verwijdering op 27-08-2027
GET /v1/work-summariesNee — wordt geaccepteerd maar genegeerd; verouderd, verwijdering op 27-08-2027
curl "https://api.kinmu.app/v1/employees?updated_since=2026-07-01T00:00:00Z" \ -H "Authorization: Bearer kinmu_sk_live_…"

Bewaar het tijdstip van je laatste synchronisatie en gebruik het als updated_since bij de volgende. Zie het volledige patroon in de BI-gids.

vacation-balances en work-summaries filteren niet op updated_since. Het zijn aggregaties: er valt geen delta op te vragen. Vraag de periode die je nodig hebt opnieuw op (year= bij saldi, from/to bij summaries) of laad hem elke ronde volledig opnieuw, en doe een upsert op sleutel in je opslag. De parameter wordt op die twee endpoints nog steeds geaccepteerd, zodat al gegenereerde SDK’s niet breken, maar hij filtert niets: hij is als verouderd gemarkeerd en verdwijnt op 27-08-2027.

Fouten

Alle fouten volgen RFC 9457 (application/problem+json):

{ "type": "https://docs.kinmu.app/errors/invalid_scope", "title": "Scope insuficiente", "status": 403, "code": "invalid_scope", "detail": "La API key no tiene el scope requerido: org:empleados:read.", "errors": { "required_scope": "org:empleados:read" } }

Programmeer tegen het veld code (stabiel), niet tegen title/detail.

Codetabel

codeHTTPWanneer
unauthenticated401De key ontbreekt, is ongeldig, ingetrokken of verlopen.
invalid_scope403De key mist de vereiste scope (errors.required_scope).
subscription_inactive403Het bedrijf heeft geen actieve dienst: opgeschort of gearchiveerd, geen abonnement, opgeschort abonnement, verlopen proefperiode, of een opgezegd en verstreken of verlopen abonnement.
addon_disabled403De addon Public API is niet actief voor het bedrijf.
validation_failed422Ongeldige body/parameters (errors met details per veld).
not_found404De resource bestaat niet of hoort bij een ander bedrijf.
conflict409Statusconflict (bijv. beslissen over een al besliste afwezigheid).
idempotency_conflict409De Idempotency-Key is hergebruikt met een andere body.
rate_limited429Limiet per minuut overschreden (zie Retry-After).
quota_exceeded429Maandelijkse quota opgebruikt.
billing_required402Het plan staat de actie niet toe (bijv. medewerker aanmaken zonder seat).
internal_error500Interne fout.

Multitenant-isolatie. Een resource van een ander bedrijf opvragen geeft 404 (not_found), nooit een 403 die het bestaan ervan verraadt.

Een opgebruikte proefperiode of een opzegging sluiten /v1 af met 403 subscription_inactive, nooit met een 402. De 402 (billing_required) is iets anders: het abonnement leeft, maar het pakket dekt de specifieke actie niet (bijv. een medewerker aanmaken zonder vrije seat). Lees de 403 als “geen dienst, heractiveren in het dashboard” en de 402 als “pakket uitbreiden”.

API-keys en webhooks intrekken (DELETE) blijft werken bij een inactief abonnement: het is een beveiligingsmaatregel, geen dienstverbruik.

Rate limits en quota

Elke response (2xx en 4xx) bevat het volgende, met twee uitzonderingen die hieronder staan:

HeaderBetekenis
X-RateLimit-LimitLimiet per minuut van de key.
X-RateLimit-RemainingResterende requests in het huidige venster.
X-RateLimit-ResetUnix-timestamp waarop het venster reset.
X-Kinmu-Quota-RemainingResterende requests van de maandelijkse quota.

Twee responses dragen deze headers niet. De 401 (unauthenticated), omdat de authenticatie vóór de throttle afkapt en de request nooit wordt geteld; en alle responses van GET /v1/reports/{report}/download, omdat de ondertekende download buiten de geauthenticeerde pipeline wordt geserveerd. Zie het ontbreken daar niet als een storing: in die twee gevallen is het zo bedoeld.

Limieten: live 120/min, test 30/min. Maandelijkse quota: live 10.000 + 1.000×actieve_medewerkers (max 100.000); test 5.000.

Overschrijd je de minuutlimiet → 429 rate_limited met Retry-After: <seconden>. Is de quota op → 429 quota_exceeded. Retry met backoff en respecteer Retry-After.

import time, requests def call_with_retry(session, method, url, **kwargs): for attempt in range(5): resp = session.request(method, url, **kwargs) if resp.status_code != 429: return resp wait = int(resp.headers.get("Retry-After", 2 ** attempt)) time.sleep(wait) resp.raise_for_status() return resp

Houd X-RateLimit-Remaining en X-Kinmu-Quota-Remaining in de gaten en spreid je requests voordat je een 429 krijgt.

Idempotentie

Bij muterende methodes (POST / PATCH / DELETE) kun je Idempotency-Key: <uniek> meesturen:

curl -s -X POST "https://api.kinmu.app/v1/check-ins" \ -H "Authorization: Bearer kinmu_sk_live_…" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: 3f9a…-uuid" \ -d '{ "employee_id": "…", "type": "in", "timestamp": "2026-07-08T08:00:00Z" }'

De eerste request wordt uitgevoerd en de response wordt 24 uur gecachet; een herhaalde request met dezelfde body krijgt de oorspronkelijke response terug (zonder opnieuw uit te voeren). Dezelfde key hergebruiken met een andere body409 idempotency_conflict.

Het aanmaken van een webhook is de enige uitzondering: het copy-once secret wordt nooit gecachet.

Last updated on