Skip to Content
Convenções

Convenções da API

Regras transversais a todos os recursos da v1.

TemaRegra
FormatoJSON UTF-8. Requests com corpo levam Content-Type: application/json.
IDsUUIDs públicos. IDs numéricos internos nunca são expostos.
DatasISO 8601. Instantes em UTC (2026-07-08T09:30:00Z); datas de calendário YYYY-MM-DD.
Base URLVersionada no path: https://api.kinmu.app/v1 · Dev: https://api.dev.kinmu.app/v1.

Paginação (cursor)

Todas as listagens paginam por cursor:

  • ?limit= (padrão 25, máx. 100) · ?cursor=<opaco>.
  • Resposta: { "data": [...], "meta": { "next_cursor": "…"|null, "has_more": true|false } }.
  • Para a próxima página, reenvie cursor=<meta.next_cursor>. has_more=false ou next_cursor=null → fim.
curl "https://api.kinmu.app/v1/employees?limit=50&cursor=eyJpZCI6MTIzfQ" \ -H "Authorization: Bearer kinmu_sk_live_…"

O cursor é opaco: não o construa nem o interprete, reenvie-o tal como veio.

Sincronização incremental (updated_since)

Todas as listagens aceitam ?updated_since=<ISO 8601> para trazer apenas o que mudou desde esse instante (polling incremental para BI e ERPs).

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

Guarde o instante da sua última sincronização e use-o como updated_since na seguinte. Veja o padrão completo no guia de BI.

Erros

Todos os erros seguem a 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" } }

Programe contra o campo code (estável), não contra title/detail.

Tabela de códigos

codeHTTPQuando
unauthenticated401Chave ausente, inválida, revogada ou expirada.
invalid_scope403A chave não tem o scope exigido (errors.required_scope).
subscription_inactive403A assinatura da empresa está suspensa/ausente.
addon_disabled403O addon Public API não está ativo para a empresa.
validation_failed422Corpo/parâmetros inválidos (errors com detalhe por campo).
not_found404O recurso não existe ou pertence a outra empresa.
conflict409Conflito de estado (por exemplo, decidir uma ausência já decidida).
idempotency_conflict409A Idempotency-Key foi reutilizada com um corpo diferente.
rate_limited429Limite por minuto excedido (veja Retry-After).
quota_exceeded429Cota mensal esgotada.
billing_required402O plano não permite a ação (por exemplo, admitir funcionário sem assento).
internal_error500Erro interno.

Isolamento multitenant. Pedir um recurso de outra empresa retorna 404 (not_found), nunca um 403 que revele sua existência.

Rate limits e cota

Cada resposta (2xx e 4xx) inclui:

HeaderSignificado
X-RateLimit-LimitLimite por minuto da chave.
X-RateLimit-RemainingRequisições restantes na janela atual.
X-RateLimit-ResetTimestamp Unix em que a janela reinicia.
X-Kinmu-Quota-RemainingRequisições restantes da cota mensal.

Limites: live 120/min, test 30/min. Cota mensal: live 10.000 + 1.000×empleados_activos (máx. 100.000); test 5.000.

Ao exceder o minuto → 429 rate_limited com Retry-After: <segundos>. Ao esgotar a cota → 429 quota_exceeded. Tente de novo com backoff respeitando 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

Acompanhe X-RateLimit-Remaining e X-Kinmu-Quota-Remaining para espaçar suas requisições antes de receber um 429.

Idempotência

Em métodos que mutam (POST / PATCH / DELETE) você pode enviar Idempotency-Key: <único>:

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

A primeira requisição é executada e sua resposta fica em cache por 24 h; um reenvio com o mesmo corpo retorna a resposta original (sem re-executar). Reutilizar a mesma chave com um corpo diferente409 idempotency_conflict.

A criação de webhook é a única exceção: seu segredo copy-once nunca vai para o cache.

Last updated on