Guide
Authentication
Every API request authenticates with an API key sent as a bearer token. A key always acts as a named member of the organisation, clipped to the scopes chosen at creation — Kelomo has no identity that belongs to nobody.
Two kinds of key
Which key you want depends on whether the integration should outlive the person who set it up.
| Personal key | Service key | |
|---|---|---|
| Who creates it | Any member, for themselves | An organisation admin (org.admin) |
| Where | Settings → your own API keys | Settings → API access |
| Endpoint | POST /v1/me/api-keys | POST /v1/api-keys/service |
| Acts as | Its creator | An explicitly named owner member |
| Scopes | Any subset of your own capabilities; may be empty | Required, non-empty, within the owner's capabilities |
| Owner can change | No — the key is that person | Yes, POST /v1/api-keys/{id}/reassign |
| When the member leaves | The key ends with them | Hand it over; the token keeps working |
A key's raw token starts with kelomo_ and is shown once at
creation — only a hash is stored, so treat the token like a password and keep it in a secret
manager. The key list shows a recognisable prefix (kelomo_ab12…), a purpose
note and rolling 7-day usage so you can tell keys apart later.
curl https://api.kelomo.fi/v1/me/entitlements \
-H "Authorization: Bearer kelomo_YOUR_KEY" Bearer keys are the authentication method for the API.
Personal keys
A personal key is the key you make for yourself: a script on your laptop, an MCP client, a spreadsheet that reads your own hours. Every member can mint one, whatever their role — a key carries its creator's identity, so an employee's key is employee-sized and an admin's key is admin-sized. It is meant to end when the membership does.
Service keys
A service key belongs to the organisation. It still executes with a real member's authority — every row-level scope in Kelomo resolves through a membership — but that ownership is explicit and transferable rather than an accident of who happened to click "create".
Choose a service key for anything that runs unattended: a nightly payroll sync, a BI refresh, a Zapier or Make scenario, an ERP bridge. A personal key would tie that job to one employment: the day its creator is deactivated, the integration stops — and it stops at 03:00, in a job nobody is watching. A service key turns that outage into a two-click handover.
# Admin session (org.admin). Both the owner and the scopes are required.
curl -X POST https://api.kelomo.fi/v1/api-keys/service \
-H "Content-Type: application/json" \
-d '{
"name": "Nightly payroll sync",
"ownerMembershipId": "OWNER_MEMBERSHIP_ID",
"capabilities": ["payroll.run", "worktime.supervise"]
}'
# → { "id": "…", "token": "kelomo_NEW…" } (shown once) Scopes are capabilities
A key's scopes are the same capability catalogue that drives permissions inside the product — there is no separate API scope vocabulary. Two rules follow from that:
-
A key can never hold a capability the member it acts as lacks. Requesting one fails with
422 key_scope_exceeds_creatorfor a personal key and422 key_scope_exceeds_ownerfor a service key — including on a reassignment, so a handover can never quietly widen what the key reaches. - At request time the effective rights are the intersection of that member's current capabilities and the key's scopes — if the member loses a capability, their keys lose it too.
Capabilities are product-area sized, for example worktime.supervise (team
worktime and approvals), scheduling.plan (shift planning),
projects.manage and projects.read, crm.use,
payroll.run (payroll and money documents), reports.team,
members.manage and integrations.manage (webhook endpoints and the
integration-event log). The key-creation screen lists exactly the capabilities available to
the member the key will act as, and each operation's requirement is stated in the
API reference.
Empty scope = self-only. A personal key created with no capabilities
reaches the caller's own /v1/me/* routes and the tenant's read-only
configuration catalogues, and nothing behind a capability gate. Broad access is always an
explicit choice of scopes, never a default. A service key may not be scope-less at all —
an empty list is refused with 422 service_key_requires_capabilities, because
a machine credential aimed at one person's own surface is a mistake worth refusing.
Personal data is out of reach of every key. Sickness certificates and other absence attachments, case notes, employee documents and profiles, recruitment candidates, occupational-safety records and whistleblowing evidence are never readable with an API key, whatever scopes it holds and whoever created it. Attachments reachable over the API belong to invoices, quotes, deals, projects, purchases and tasks.
Read-only keys
Mark a key read-only to make it structurally safe for dashboards, reporting and AI
assistants: any request other than GET is refused with
403 read_only_key, whatever the key's scopes say.
Expiry
Keys can carry an expiry instant — the creation UI defaults to one year, and "never" is an
explicit choice. Past the expiry the key stops authenticating with
401 api_key_expired. Prefer expiring keys plus rotation over immortal ones.
Rotation
Rotate a key without downtime: the rotate call returns a new token (shown once), and the old token keeps working for a 24-hour grace period while you roll out the new one.
Rotation is owner-only. It lives on /v1/me/api-keys/{id}/rotate
and works on your own keys, from a human session — an API key may never manage API keys. An
admin cannot rotate somebody else's key, because a rotated token is handed to whoever makes
the call, and that would be a quiet way to obtain a credential in another member's name. The
admin lever over another member's key is revocation, which takes authority away
instead of moving it: DELETE /v1/api-keys/{id} works on any key in the
organisation.
curl -X POST https://api.kelomo.fi/v1/me/api-keys/KEY_ID/rotate \
-H "Authorization: Bearer kelomo_YOUR_SESSION"
# → { "token": "kelomo_NEW…" } (shown once; the old token works for 24 h) When the owner leaves: 401 api_key_owner_inactive
A key stops authenticating the moment the member it acts as is deactivated. That is a
distinct code, not a generic invalid_token, precisely so an integration can
tell "my credential is wrong" from "the person behind my credential left":
- Service key. Reassign it to another member and the same token resumes working — the scopes, the usage history and the audit trail all survive; only the executing identity moves. The new owner's capabilities are re-checked against the key's scopes, so the handover cannot widen its reach.
- Personal key. Nothing to fix — a personal key is meant to end with its member. Revoke it, and if the job it did must keep running, replace it with a service key.
# Admin session (org.admin). The token and the scopes are unchanged.
curl -X POST https://api.kelomo.fi/v1/api-keys/KEY_ID/reassign \
-H "Content-Type: application/json" \
-d '{ "ownerMembershipId": "NEW_OWNER_MEMBERSHIP_ID" }' Deactivating a member is never blocked by the keys they hold — offboarding must not be stoppable — so the admin's key list flags every key whose owner is inactive, and Settings → API access is where a handover happens.
Rate limits
Keyed traffic is limited per key — 600 requests per minute by default — with an aggregate cap per organisation, so one runaway integration cannot starve the others. Every keyed response reports where you stand:
HTTP/1.1 200 OK
X-RateLimit-Limit: 600
X-RateLimit-Remaining: 574
X-RateLimit-Reset: 1770300060 | Header | Meaning |
|---|---|
X-RateLimit-Limit | Requests allowed per key in the current one-minute window. |
X-RateLimit-Remaining | Requests left in the window. |
X-RateLimit-Reset | Unix timestamp (seconds) when the window resets. |
Exceeding a limit returns 429 with a Retry-After header — wait
that many seconds before retrying, and back off exponentially on repeated 429s rather than
hammering the window boundary.
Auditing
Changes made with an API key are recorded in the organisation's audit trail with the key's identity attached, so an integration's writes are always distinguishable from the member's own actions.