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)

O parâmetro ?updated_since=<ISO 8601> traz apenas o que mudou desde esse instante (polling incremental para BI e ERPs). Não é universal: só filtra nestas listagens.

ListagemFiltra por updated_since?
GET /v1/employeesSim
GET /v1/check-insSim
GET /v1/absencesSim
GET /v1/locationsSim
GET /v1/unitsSim
GET /v1/webhook-endpointsSim
GET /v1/vacation-balancesNão — aceite mas ignorado; obsoleto, remoção a 27-08-2027
GET /v1/work-summariesNão — aceite mas ignorado; obsoleto, remoção a 27-08-2027
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.

vacation-balances e work-summaries não filtram por updated_since. São agregados: não há delta para pedir. Volte a consultar o intervalo que lhe interessa (year= nos saldos, from/to nos resumos) ou recarregue-o por inteiro em cada passagem, e faça upsert por chave no seu armazenamento. O parâmetro continua a ser aceite nesses dois endpoints, para não partir os SDK já gerados, mas não filtra nada: está marcado como obsoleto e será removido a 27-08-2027.

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 empresa não tem serviço ativo: suspensa ou arquivada, sem subscrição, subscrição suspensa, período experimental esgotado, ou subscrição cancelada e já terminada ou expirada.
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.

Um período experimental esgotado ou um cancelamento cortam o /v1 com 403 subscription_inactive, nunca com um 402. O 402 (billing_required) é outra coisa: a subscrição está viva, mas o plano não chega para a ação concreta (p. ex. criar um colaborador sem lugar disponível). Leia o 403 como «não há serviço, é preciso reativar no painel» e o 402 como «é preciso aumentar o plano».

Revogar chaves de API e webhooks (DELETE) continua a funcionar com a subscrição inativa: é um controlo de segurança, não consumo do serviço.

Rate limits e cota

Cada resposta (2xx e 4xx) inclui, com duas exceções documentadas mais abaixo:

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.

Duas respostas não levam estes cabeçalhos. As 401 (unauthenticated), porque a autenticação corta antes do throttle e o pedido nunca chega a ser contado; e todas as respostas de GET /v1/reports/{report}/download, porque a descarga assinada é servida fora do pipeline autenticado. Não trate a sua ausência como falha: nesses dois casos é o esperado.

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