Skip to Content
Konventionen

API-Konventionen

Regeln, die für alle Ressourcen von v1 gelten.

ThemaRegel
FormatJSON UTF-8. Requests mit Body tragen Content-Type: application/json.
IDsÖffentliche UUIDs. Interne numerische IDs werden nie exponiert.
DatumsangabenISO 8601. Zeitpunkte in UTC (2026-07-08T09:30:00Z); Kalenderdaten als YYYY-MM-DD.
Base URLVersioniert im Pfad: https://api.kinmu.app/v1 · Dev: https://api.dev.kinmu.app/v1.

Paginierung (Cursor)

Alle Listen paginieren per Cursor:

  • ?limit= (Default 25, max. 100) · ?cursor=<opaco>.
  • Antwort: { "data": [...], "meta": { "next_cursor": "…"|null, "has_more": true|false } }.
  • Für die nächste Seite sendest du cursor=<meta.next_cursor>. has_more=false oder next_cursor=null → Ende.
curl "https://api.kinmu.app/v1/employees?limit=50&cursor=eyJpZCI6MTIzfQ" \ -H "Authorization: Bearer kinmu_sk_live_…"

Der cursor ist opak: Baue ihn nicht selbst zusammen und parse ihn nicht — sende ihn unverändert zurück.

Inkrementelle Synchronisierung (updated_since)

Der Parameter ?updated_since=<ISO 8601> lädt nur die Änderungen seit diesem Zeitpunkt (inkrementelles Polling für BI und ERPs). Er gilt nicht überall: Nur bei diesen Listen filtert er.

ListeFiltert nach 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-balancesNein — wird akzeptiert, aber ignoriert; veraltet, Entfernung am 27.08.2027
GET /v1/work-summariesNein — wird akzeptiert, aber ignoriert; veraltet, Entfernung am 27.08.2027
curl "https://api.kinmu.app/v1/employees?updated_since=2026-07-01T00:00:00Z" \ -H "Authorization: Bearer kinmu_sk_live_…"

Speichere den Zeitpunkt deiner letzten Synchronisierung und verwende ihn beim nächsten Lauf als updated_since. Das vollständige Pattern findest du im BI-Guide.

vacation-balances und work-summaries filtern nicht nach updated_since. Es sind Aggregate: Es gibt kein Delta, das man anfordern könnte. Frage stattdessen den Zeitraum erneut ab, der dich interessiert (year= bei Salden, from/to bei Summaries), oder lade ihn bei jedem Lauf komplett neu — und mache in deinem Speicher ein Upsert über den Schlüssel. Der Parameter wird an diesen beiden Endpoints weiterhin akzeptiert, damit bereits generierte SDKs nicht brechen, filtert aber nichts: Er ist als veraltet markiert und wird am 27.08.2027 entfernt.

Fehler

Alle Fehler folgen 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" } }

Programmiere gegen das Feld code (stabil), nicht gegen title/detail.

Fehlercodes

codeHTTPWann
unauthenticated401Key fehlt, ist ungültig, widerrufen oder abgelaufen.
invalid_scope403Der Key hat den erforderlichen Scope nicht (errors.required_scope).
subscription_inactive403Das Unternehmen hat keinen aktiven Service: ausgesetzt oder archiviert, kein Abonnement, ausgesetztes Abonnement, abgelaufene Testphase oder ein gekündigtes und abgelaufenes bzw. verfallenes Abonnement.
addon_disabled403Das Addon Public API ist für das Unternehmen nicht aktiv.
validation_failed422Ungültiger Body/Parameter (errors mit Details pro Feld).
not_found404Die Ressource existiert nicht oder gehört einem anderen Unternehmen.
conflict409Zustandskonflikt (z. B. eine bereits entschiedene Abwesenheit erneut entscheiden).
idempotency_conflict409Die Idempotency-Key wurde mit einem anderen Body wiederverwendet.
rate_limited429Minutenlimit überschritten (siehe Retry-After).
quota_exceeded429Monatskontingent aufgebraucht.
billing_required402Der Plan erlaubt die Aktion nicht (z. B. Mitarbeitenden anlegen ohne freien Seat).
internal_error500Interner Fehler.

Multitenant-Isolation. Eine Ressource eines anderen Unternehmens anzufragen liefert 404 (not_found) — nie ein 403, das ihre Existenz verraten würde.

Eine abgelaufene Testphase oder eine Kündigung sperren /v1 mit 403 subscription_inactive — nie mit einem 402. Der 402 (billing_required) ist etwas anderes: Das Abonnement lebt, aber der Tarif deckt die konkrete Aktion nicht ab (z. B. eine Person anlegen, ohne freien Platz). Lies den 403 als „kein Service, im Dashboard reaktivieren“ und den 402 als „Tarif erweitern“.

API-Keys und Webhooks zu widerrufen (DELETE) funktioniert auch bei inaktivem Abonnement weiter: Das ist eine Sicherheitsmaßnahme, keine Nutzung des Service.

Rate Limits und Kontingent

Jede Antwort (2xx und 4xx) enthält Folgendes — mit zwei Ausnahmen, die weiter unten stehen:

HeaderBedeutung
X-RateLimit-LimitMinutenlimit des Keys.
X-RateLimit-RemainingVerbleibende Requests im aktuellen Fenster.
X-RateLimit-ResetUnix-Timestamp, zu dem das Fenster zurückgesetzt wird.
X-Kinmu-Quota-RemainingVerbleibende Requests des Monatskontingents.

Zwei Antworten tragen diese Header nicht. Die 401 (unauthenticated), weil die Authentifizierung vor dem Throttle greift und der Request gar nicht gezählt wird; und alle Antworten von GET /v1/reports/{report}/download, weil der signierte Download außerhalb der authentifizierten Pipeline ausgeliefert wird. Werte ihr Fehlen dort nicht als Fehler — in diesen beiden Fällen ist es erwartet.

Limits: live 120/min, test 30/min. Monatskontingent: live 10.000 + 1.000×empleados_activos (max. 100.000); test 5.000.

Beim Überschreiten des Minutenlimits → 429 rate_limited mit Retry-After: <segundos>. Bei aufgebrauchtem Kontingent → 429 quota_exceeded. Wiederhole mit Backoff und respektiere 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

Behalte X-RateLimit-Remaining und X-Kinmu-Quota-Remaining im Blick, um deine Requests zu verteilen, bevor du ein 429 bekommst.

Idempotenz

Bei mutierenden Methoden (POST / PATCH / DELETE) kannst du Idempotency-Key: <único> senden:

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" }'

Der erste Request wird ausgeführt und seine Antwort 24 h gecacht; ein erneutes Senden mit dem gleichen Body liefert die ursprüngliche Antwort (ohne erneute Ausführung). Denselben Key mit einem anderen Body wiederverwenden → 409 idempotency_conflict.

Das Anlegen eines Webhooks ist die einzige Ausnahme: Sein copy-once-Secret wird nie gecacht.

Last updated on