API Reference
Departments
List departments and fetch a single department's snapshot, with live mode-aware cost rolled up from its teams.
Two endpoints cover the department surface: a paginated list and a single-department lookup by UUID. A department's cost.current_run_rate_hourly, assigned_workloads, and assigned_pvs are the sum of its teams' values, counting distinct entities — a workload split across two teams in the same department counts as 1; a workload split across teams in different departments contributes to both. A team without a department is valid; it does not roll up to any department total. Both endpoints accept ?cost_mode= and return genuinely different numbers per mode (see Cost Modes).
The list sorts by cost.current_run_rate_hourly descending. Pagination is cursor-based; see Pagination & Filtering.
List departments
GET /v1/departments
Required scope: departments:read
Department inventory across the tenant, sorted by hourly run-rate cost descending.
Query parameters
| Name | Type | Default | Allowed values | Description |
|---|---|---|---|---|
limit | integer | 100 | 1..500 | Page size. |
cursor | string | Opaque pagination token from a previous response. | ||
cost_mode | string | fully_loaded | fully_loaded, workload_only | Cost attribution mode. See Cost Modes. |
origin | string (csv) | Common: k8s, kubeadapt | Restrict by where the department was created. Not strictly validated — any string is accepted. |
Example request
curl -H "Authorization: Bearer ka_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
"https://public-api.kubeadapt.io/v1/departments?cost_mode=fully_loaded&limit=20"Example response
1{
2 "data": [
3 {
4 "id": "e1f2a3b4-c5d6-4e7f-a8b9-c0d1e2f3a4b5",
5 "kind": "Department",
6 "metadata": {
7 "name": "Engineering",
8 "description": "All engineering teams: platform, payments, data, growth.",
9 "origin": "kubeadapt",
10 "owner_email": "vp-engineering@acme.io",
11 "created_at": "2025-08-14T10:00:00Z",
12 "updated_at": "2026-05-04T16:20:00Z"
13 },
14 "teams": 14,
15 "assigned_workloads": 247,
16 "assigned_pvs": 38,
17 "cost": {
18 "current_run_rate_hourly": { "amount": "84.1200", "currency": "USD" },
19 "cost_mode": "fully_loaded"
20 }
21 },
22 {
23 "id": "5d6e7f8a-9b0c-4d1e-8f23-4a5b6c7d8e9f",
24 "kind": "Department",
25 "metadata": {
26 "name": "Data",
27 "description": "Analytics, ML, and the data warehouse.",
28 "origin": "kubeadapt",
29 "owner_email": "data-lead@acme.io",
30 "created_at": "2025-08-14T10:00:00Z",
31 "updated_at": "2026-04-30T12:11:00Z"
32 },
33 "teams": 4,
34 "assigned_workloads": 62,
35 "assigned_pvs": 8,
36 "cost": {
37 "current_run_rate_hourly": { "amount": "24.6800", "currency": "USD" },
38 "cost_mode": "fully_loaded"
39 }
40 }
41 ],
42 "meta": {
43 "request_id": "req_01J5K3V0Q7Y4XR8A2B3C5D7D01",
44 "applied_at": "2026-05-21T10:30:00Z",
45 "pagination": {
46 "next_cursor": "",
47 "has_more": false,
48 "limit": 20
49 },
50 "cost_mode": "fully_loaded"
51 }
52}Example error response
1{
2 "data": null,
3 "meta": {
4 "request_id": "req_01J5K3V0Q7Y4XR8A2B3C5D7D02",
5 "applied_at": "2026-05-21T10:30:05Z"
6 },
7 "error": {
8 "code": "DEPARTMENT_NOT_FOUND",
9 "message": "department not found",
10 "details": []
11 }
12}Common errors
| HTTP | error.code | When |
|---|---|---|
| 400 | BAD_REQUEST | Malformed limit or cursor. |
| 401 | UNAUTHORIZED | Missing or invalid Bearer token. See Authentication. |
| 403 | FORBIDDEN | Token lacks departments:read. See Permission Scopes. |
| 410 | CURSOR_EXPIRED | Cursor older than 24h or query parameters changed since issue. |
| 422 | INVALID_COST_MODE | cost_mode value not in fully_loaded, workload_only. |
| 429 | RATE_LIMITED | Per-key quota exceeded. See Retry-After header. See Error Handling. |
Get department by ID
GET /v1/departments/{dept_id}
Required scope: departments:read
Single department body. Same shape as the list elements.
Path parameters
| Name | Type | Description |
|---|---|---|
dept_id | UUID | Department identifier returned by the list endpoint. |
Query parameters
| Name | Type | Default | Allowed values | Description |
|---|---|---|---|---|
cost_mode | string | fully_loaded | fully_loaded, workload_only | Cost attribution mode. See Cost Modes. |
Example request
curl -H "Authorization: Bearer ka_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
"https://public-api.kubeadapt.io/v1/departments/e1f2a3b4-c5d6-4e7f-a8b9-c0d1e2f3a4b5?cost_mode=workload_only"Example response
1{
2 "data": {
3 "id": "e1f2a3b4-c5d6-4e7f-a8b9-c0d1e2f3a4b5",
4 "kind": "Department",
5 "metadata": {
6 "name": "Engineering",
7 "description": "All engineering teams: platform, payments, data, growth.",
8 "origin": "kubeadapt",
9 "owner_email": "vp-engineering@acme.io",
10 "created_at": "2025-08-14T10:00:00Z",
11 "updated_at": "2026-05-04T16:20:00Z"
12 },
13 "teams": 14,
14 "assigned_workloads": 247,
15 "assigned_pvs": 38,
16 "cost": {
17 "current_run_rate_hourly": { "amount": "64.7800", "currency": "USD" },
18 "cost_mode": "workload_only"
19 }
20 },
21 "meta": {
22 "request_id": "req_01J5K3V0Q7Y4XR8A2B3C5D7D03",
23 "applied_at": "2026-05-21T10:30:00Z",
24 "cost_mode": "workload_only"
25 }
26}Same department as the list example, fetched in workload_only mode. The amount drops from 84.1200 to 64.7800 per hour — the delta is the cluster overhead excluded by workload_only.
Common errors
| HTTP | error.code | When |
|---|---|---|
| 400 | BAD_REQUEST | dept_id is not a valid UUID. |
| 401 | UNAUTHORIZED | Missing or invalid Bearer token. |
| 403 | FORBIDDEN | Token lacks departments:read. |
| 404 | DEPARTMENT_NOT_FOUND | No department with this ID exists in the tenant. |
| 422 | INVALID_COST_MODE | cost_mode value not in fully_loaded, workload_only. |
| 429 | RATE_LIMITED | Per-key quota exceeded. |
Field notes
teamsis the count of teams whosemetadata.department.idequals this department'sid— not workloads, not assignments. Enumerate withGET /v1/teams?department_id=.originmatches the Team semantics:kubeadaptfor departments created in the UI,k8sfor departments mirrored from a configured Kubernetes label key.- Department bodies have no
last_seen_at. The field exists on Team to detect retired teams; departments do not go stale. - Deleting a department orphans its teams rather than deleting them. Orphaned teams have no
metadata.departmentblock; their cost stops rolling up.
Filtering patterns
GET /v1/departments # all, biggest first
GET /v1/departments?cost_mode=workload_only # exclude overhead
GET /v1/departments/{dept_id} # rollup
GET /v1/teams?department_id={dept_id} # child teams
GET /v1/departments?origin=k8s # mirrored from labelsSee also
- Teams: the layer below departments, including assignments.
- Cost Modes. Fully Loaded vs Workload Only, with worked examples.
- Cost Attribution: Overview: the model behind departments, teams, and assignments.
- Teams and Departments: the data model and lifecycle, including what happens on delete.
- Cost Explorer: aggregated cost queries grouped by namespace, workload, or any other dimension — pair with
cost_modefor live mode-aware totals.