Endpoint reference
Every route, and the scope it needs. This table is generated from the routes the API actually serves, so it cannot describe an endpoint that does not exist or omit one that does.
| Endpoint | Scope required | Filters | Text search |
|---|---|---|---|
GET /api/v1/assetsGET /api/v1/assets/{id} | assets:read | category, location_id, status | yes |
GET /api/v1/handoversGET /api/v1/handovers/{id} | handovers:read | location_id, status | yes |
GET /api/v1/kpi-resultsGET /api/v1/kpi-results/{id} | kpi:read | metric_id | — |
GET /api/v1/kpisGET /api/v1/kpis/{id} | kpi:read | cadence, function_category, location_id | yes |
GET /api/v1/learning-assignmentsGET /api/v1/learning-assignments/{id} | learning:read | — | — |
GET /api/v1/locationsGET /api/v1/locations/{id} | locations:read | — | yes |
GET /api/v1/peopleGET /api/v1/people/{id} | people:read | location_id | yes |
GET /api/v1/purchase-requestsGET /api/v1/purchase-requests/{id} | approvals:read | location_id | yes |
GET /api/v1/systemsGET /api/v1/systems/{id} | systems:read | category, location_id, status | yes |
GET /api/v1/tasksGET /api/v1/tasks/{id} | tasks:read | location_id | yes |
GET /api/v1/transitionsGET /api/v1/transitions/{id} | transitions:read | location_id, status | yes |
GET /api/v1/vendorsGET /api/v1/vendors/{id} | vendors:read | category, location_id, status | yes |
GET /api/v1/workplace-recordsGET /api/v1/workplace-records/{id} | workplace:read | location_id, record_type, status | yes |
| Write | Scope required | Body |
|---|---|---|
POST /api/v1/kpi-results | kpi:record | metric_id, period_start, actual, target (optional) |
Common parameters
Every list accepts:
limit— 1 to 1000, default 100cursor— thenext_cursorfrom the previous pageupdated_since— an ISO 8601 timestamp; returns only records changed after it
A register accepts search only where the table above says so, and only the filters listed against it. A parameter an endpoint does not accept is a 422, and a value outside the set it does accept is a 400 — neither is an empty page, because a page you cannot tell from "no matches" is worse than an error.
Recording a KPI result
POST /api/v1/kpi-results with a key holding kpi:record records the result of one closed reporting period, or corrects the one already there.
curl https://your-workspace.kitset.io/api/v1/kpi-results \
-H "Authorization: Bearer kitset_sk_live_YOUR_KEY_ID_HERE" \
-H "Content-Type: application/json" \
-d '{"metric_id": "9b2c...", "period_start": "2026-09-01", "actual": 28.5}'
metric_idis theidof an active KPI in this workspace, fromGET /api/v1/kpis. Any other — archived, deleted, or another workspace's — is a404, and the three are never told apart.period_startis the first day of one of the KPI's reporting periods: a Monday for a weekly KPI, the 1st for a monthly one, the first day of a quarter for a quarterly one. The period must have ended where the KPI is reported; otherwise the answer is400.actualis the result, up to four decimal places. Send it as a JSON number or a string; either is read exactly, never through a binary float.targetis optional. Leave it out and a new result is judged against the KPI's current target, while a correction keeps the target its period was judged against.
The answer is the result as GET /api/v1/kpi-results/{id} returns it, with operation set to created (status 201), or updated or unchanged (both 200). Sending the same request twice records it once, so a request that timed out can simply be sent again.
There is no note: like every field this API serves, a write carries no free text. The result is stored without an author — a key is not a person — and the workspace audit trail names the key id instead. The workspace's KPI Scorecards app must be installed (403 otherwise), and a workspace whose subscription has lapsed answers 402. The body may be at most 4096 bytes, must declare its Content-Length, and must arrive within ten seconds. A workspace busy with other work, such as a large import, answers 503 with a Retry-After; send the same request again.
Scopes
| Scope | Covers | Access | What it allows |
|---|---|---|---|
vendors:read | Vendors & Renewals | Read | Subscriptions, their owners, spend, seats, renewal dates and notice periods. |
approvals:read | Requests & Approvals | Read | Purchase requests, their approvers and the decisions taken on them. |
systems:read | Systems & Access | Read | Business systems, their owners and access-review dates. |
learning:read | Learning & Compliance | Read | Assignments: who was given which content, and their progress, score and completion. |
handovers:read | Handover & Continuity | Read | Holiday, shift and role handovers: who handed over to whom, when, and their status. |
transitions:read | Employee Transitions | Read | Joiner and leaver checklists: who each is for, who coordinates it, its dates and status. |
assets:read | Assets & Maintenance | Read | Equipment, who holds it now, and its warranty and maintenance dates. |
workplace:read | Workplace Operations | Read | Workplace records and the people they concern. |
people:read | People | Read | The employee directory: names, work contact details, roles and teams. |
locations:read | Locations | Read | Sites, departments and teams. |
tasks:read | Tasks | Read | The shared task list: work generated by handovers and learning assignments, and work people have written by hand. |
kpi:read | KPI Scorecards | Read | KPI definitions, targets and owners, and each closed period's recorded result. |
kpi:record | Record KPI results | Write | Record or correct a closed period's result for any active KPI. Write only: reading needs kpi:read as well. |
The machine-readable contract
/api/v1/openapi.json describes this surface as OpenAPI, and is generated from the same routes. Use it to build a client rather than typing one out.
It describes /api/v1 only. kitset's own browser client talks to other endpoints; those are internal, they change without notice, and they are not part of this contract.