Skip to Content
Convenzioni

Convenzioni dell’API

Regole trasversali a tutte le risorse della v1.

TemaRegola
FormatoJSON UTF-8. Le richieste con body portano Content-Type: application/json.
IDUUID pubblici. Gli ID numerici interni non vengono mai esposti.
DateISO 8601. Istanti in UTC (2026-07-08T09:30:00Z); date di calendario YYYY-MM-DD.
Base URLVersionata nel path: https://api.kinmu.app/v1 · Dev: https://api.dev.kinmu.app/v1.

Paginazione (cursore)

Tutti gli elenchi sono paginati per cursore:

  • ?limit= (default 25, max 100) · ?cursor=<opaco>.
  • Risposta: { "data": [...], "meta": { "next_cursor": "…"|null, "has_more": true|false } }.
  • Per la pagina successiva, reinvia cursor=<meta.next_cursor>. has_more=false o next_cursor=null → fine.
curl "https://api.kinmu.app/v1/employees?limit=50&cursor=eyJpZCI6MTIzfQ" \ -H "Authorization: Bearer kinmu_sk_live_…"

Il cursor è opaco: non costruirlo né parsarlo, reinvialo così com’è.

Sincronizzazione incrementale (updated_since)

Tutti gli elenchi accettano ?updated_since=<ISO 8601> per ottenere solo ciò che è cambiato da quell’istante (polling incrementale per BI ed ERP).

curl "https://api.kinmu.app/v1/employees?updated_since=2026-07-01T00:00:00Z" \ -H "Authorization: Bearer kinmu_sk_live_…"

Salva l’istante dell’ultima sincronizzazione e usalo come updated_since nella successiva. Trovi il pattern completo nella guida BI.

Errori

Tutti gli errori sono 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" } }

Programma sul campo code (stabile), non su title/detail.

Tabella dei codici

codeHTTPQuando
unauthenticated401Chiave assente, non valida, revocata o scaduta.
invalid_scope403La chiave non ha lo scope richiesto (errors.required_scope).
subscription_inactive403L’abbonamento dell’azienda è sospeso o assente.
addon_disabled403L’addon Public API non è attivo per l’azienda.
validation_failed422Body o parametri non validi (errors con il dettaglio per campo).
not_found404La risorsa non esiste o appartiene a un’altra azienda.
conflict409Conflitto di stato (ad es. decidere un’assenza già decisa).
idempotency_conflict409La Idempotency-Key è stata riusata con un body diverso.
rate_limited429Limite al minuto superato (vedi Retry-After).
quota_exceeded429Quota mensile esaurita.
billing_required402Il piano non consente l’azione (ad es. creare un dipendente senza posti disponibili).
internal_error500Errore interno.

Isolamento multitenant. Richiedere una risorsa di un’altra azienda restituisce 404 (not_found), mai un 403 che ne riveli l’esistenza.

Rate limit e quota

Ogni risposta (2xx e 4xx) include:

HeaderSignificato
X-RateLimit-LimitLimite al minuto della chiave.
X-RateLimit-RemainingRichieste rimanenti nella finestra corrente.
X-RateLimit-ResetTimestamp Unix in cui la finestra si azzera.
X-Kinmu-Quota-RemainingRichieste rimanenti della quota mensile.

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

Superato il limite al minuto → 429 rate_limited con Retry-After: <secondi>. Esaurita la quota → 429 quota_exceeded. Riprova con backoff rispettando 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

Tieni d’occhio X-RateLimit-Remaining e X-Kinmu-Quota-Remaining per distanziare le richieste prima di ricevere un 429.

Idempotenza

Nei metodi che mutano (POST / PATCH / DELETE) puoi inviare Idempotency-Key: <unico>:

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

La prima richiesta viene eseguita e la sua risposta resta in cache per 24 h; un reinvio con lo stesso body restituisce la risposta originale (senza ri-esecuzione). Riusare la stessa chiave con un body diverso409 idempotency_conflict.

La creazione di un webhook è l’unica eccezione: il suo secret copy-once non viene mai messo in cache.

Last updated on