API Reference
Teams
List teams, fetch a single team's snapshot, and enumerate the workload and namespace assignments a team owns.
Three endpoints cover the team surface: a paginated list, a single-team lookup by UUID, and a paginated list of the workloads and namespaces a team owns. Teams sit one level above a workload and one level below a department. The list and detail endpoints accept ?cost_mode= and return genuinely different numbers per mode; the assignments endpoint does not accept ?cost_mode= (assignment rows carry no cost.* fields). See Cost Modes.
Each assignment binds one entity (a workload UID or a namespace name) to one team with a weight_percentage. An entity can be split across up to ten teams; the weights for that entity sum to 100. /v1/teams/{team_id}/assignments returns one row per (entity, team) pair — a namespace checkout split 60/40 across Payments and Platform shows up as two assignment rows, one in each team's list. The model is documented in Cost Attribution: Assignment Workbench.
The team list sorts by cost.current_run_rate_hourly descending. Pagination is cursor-based; see Pagination & Filtering.
List teams
GET /v1/teams
Required scope: teams:read
Team inventory across the tenant, sorted by hourly run-rate cost descending. The cost number is live, recomputed per request against the current month-to-date window.
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. |
department_id | string (csv) | UUID list | Restrict to teams in specific departments. Teams without a department are excluded when this filter is set. | |
origin | string (csv) | Common: k8s, kubeadapt | Restrict by where the team was created. k8s means the team was mirrored from a Kubernetes label; kubeadapt means it was created in the UI. Not strictly validated — any string is accepted. |
Example request
curl -H "Authorization: Bearer ka_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
"https://public-api.kubeadapt.io/v1/teams?cost_mode=fully_loaded&limit=50"Example response
1{
2 "data": [
3 {
4 "id": "7a3b9c12-4d8e-4f01-bc2a-9e8f7a6b5c4d",
5 "kind": "Team",
6 "metadata": {
7 "name": "Payments",
8 "description": "Owns checkout, cart, billing, and the payment-gateway integration.",
9 "origin": "kubeadapt",
10 "owner_email": "payments-lead@acme.io",
11 "department": {
12 "id": "e1f2a3b4-c5d6-4e7f-a8b9-c0d1e2f3a4b5",
13 "name": "Engineering"
14 },
15 "created_at": "2025-11-12T14:22:00Z",
16 "updated_at": "2026-05-14T08:01:00Z",
17 "last_seen_at": "2026-05-21T10:29:00Z"
18 },
19 "assigned_workloads": 28,
20 "assigned_pvs": 4,
21 "cost": {
22 "current_run_rate_hourly": { "amount": "12.4000", "currency": "USD" },
23 "cost_mode": "fully_loaded"
24 }
25 },
26 {
27 "id": "1f8e7d6c-5b4a-4039-8271-6e5d4c3b2a1f",
28 "kind": "Team",
29 "metadata": {
30 "name": "Data Platform",
31 "description": "Stream processing, batch ETL, and the feature store.",
32 "origin": "k8s",
33 "owner_email": "data-platform@acme.io",
34 "department": {
35 "id": "e1f2a3b4-c5d6-4e7f-a8b9-c0d1e2f3a4b5",
36 "name": "Engineering"
37 },
38 "created_at": "2025-09-04T09:11:00Z",
39 "updated_at": "2026-05-19T17:42:00Z",
40 "last_seen_at": "2026-05-21T10:29:00Z"
41 },
42 "assigned_workloads": 42,
43 "assigned_pvs": 6,
44 "cost": {
45 "current_run_rate_hourly": { "amount": "18.7200", "currency": "USD" },
46 "cost_mode": "fully_loaded"
47 }
48 }
49 ],
50 "meta": {
51 "request_id": "req_01J5K3V0Q7Y4XR8A2B3C5D7T01",
52 "applied_at": "2026-05-21T10:30:00Z",
53 "pagination": {
54 "next_cursor": "eyJ2IjoxLCJhZnRlcl9pZCI6IjFmOGU3ZDZjLTViNGEtNDAzOS04MjcxLTZlNWQ0YzNiMmExZiIsImFmdGVyX3ZhbHVlIjoiRGF0YSBQbGF0Zm9ybSIsInF1ZXJ5X2hhc2giOiJhM2I0YzVkNmU3ZjhnOWEwYjFjMmQzZTRmNWc2IiwiaXNzdWVkX2F0IjoxNzE2Mjg1MDAwfQ",
55 "has_more": true,
56 "limit": 50
57 },
58 "cost_mode": "fully_loaded"
59 }
60}Example error response
1{
2 "data": null,
3 "meta": {
4 "request_id": "req_01J5K3V0Q7Y4XR8A2B3C5D7T02",
5 "applied_at": "2026-05-21T10:30:05Z"
6 },
7 "error": {
8 "code": "TEAM_NOT_FOUND",
9 "message": "team not found",
10 "details": []
11 }
12}Common errors
| HTTP | error.code | When |
|---|---|---|
| 400 | BAD_REQUEST | Malformed limit, malformed cursor, malformed department_id UUID. |
| 401 | UNAUTHORIZED | Missing or invalid Bearer token. See Authentication. |
| 403 | FORBIDDEN | Token lacks teams: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 team by ID
GET /v1/teams/{team_id}
Required scope: teams:read
Single team body. Same shape as the list elements.
Path parameters
| Name | Type | Description |
|---|---|---|
team_id | UUID | Team 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/teams/7a3b9c12-4d8e-4f01-bc2a-9e8f7a6b5c4d?cost_mode=workload_only"Example response
Returns a single Team object. Same shape as one element of the List teams response above, wrapped directly under data (no array, no pagination in meta). The cost amount differs from the list example when a different cost_mode is requested — fully_loaded and workload_only return materially different numbers.
1{
2 "data": {
3 "id": "7a3b9c12-4d8e-4f01-bc2a-9e8f7a6b5c4d",
4 "kind": "Team",
5 "metadata": { "name": "Payments", "origin": "kubeadapt", "owner_email": "payments-lead@acme.io", "department": { "id": "e1f2a3b4-c5d6-4e7f-a8b9-c0d1e2f3a4b5", "name": "Engineering" }, "...": "..." },
6 "assigned_workloads": 28,
7 "assigned_pvs": 4,
8 "cost": { "current_run_rate_hourly": { "amount": "9.6300", "currency": "USD" }, "cost_mode": "workload_only" }
9 },
10 "meta": {
11 "request_id": "req_01J5K3V0Q7Y4XR8A2B3C5D7T03",
12 "applied_at": "2026-05-21T10:30:00Z",
13 "cost_mode": "workload_only"
14 }
15}For the complete field list, see the canonical example in List teams or the OpenAPI spec.
Common errors
| HTTP | error.code | When |
|---|---|---|
| 400 | BAD_REQUEST | team_id is not a valid UUID. |
| 401 | UNAUTHORIZED | Missing or invalid Bearer token. |
| 403 | FORBIDDEN | Token lacks teams:read. |
| 404 | TEAM_NOT_FOUND | No team 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. |
List team assignments
GET /v1/teams/{team_id}/assignments
Required scope: teams:read
List of TeamAssignment records for this team. An assignment binds an entity, either a workload UID or a namespace name, to the team with a weight_percentage. The percentage is the team's share of that entity's cost; when one entity is split across multiple teams, the percentages across all owning teams sum to 100.
The schema carries id, kind, and metadata. There are no cost.* fields on this resource; the cost lives on the parent team (and on the workload or namespace the assignment points to). The endpoint does not accept ?cost_mode=; the parameter is not validated or echoed.
Path parameters
| Name | Type | Description |
|---|---|---|
team_id | UUID | Team identifier returned by the list endpoint. |
Query parameters
| Name | Type | Default | Allowed values | Description |
|---|---|---|---|---|
limit | integer | 100 | 1..500 | Page size. |
cursor | string | Opaque pagination token from a previous response. | ||
entity_type | string (csv) | Common: workload, namespace | Restrict by entity kind. Unknown values return no results. | |
cluster_id | string (csv) | UUID list | Restrict by cluster. Useful for chargeback reports scoped to one production cluster. | |
source | string (csv) | Common: user_manual, k8s_label, namespace_auto, backfill_v1 | Restrict by how the assignment was created. user_manual is the Assignment Workbench; k8s_label is the label reconciler; namespace_auto is the cluster-onboarding default. Not strictly validated. |
Example request
curl -H "Authorization: Bearer ka_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
"https://public-api.kubeadapt.io/v1/teams/7a3b9c12-4d8e-4f01-bc2a-9e8f7a6b5c4d/assignments?source=user_manual,k8s_label&limit=50"Example response
1{
2 "data": [
3 {
4 "id": "2c4e6a8b-1d3f-4051-9273-6b8a0c2d4e6f",
5 "kind": "TeamAssignment",
6 "metadata": {
7 "team": {
8 "id": "7a3b9c12-4d8e-4f01-bc2a-9e8f7a6b5c4d",
9 "name": "Payments"
10 },
11 "cluster": {
12 "id": "c1a2b3c4-d5e6-7890-abcd-ef1234567890",
13 "name": "prod-eu-west-1"
14 },
15 "entity_type": "workload",
16 "entity_identifier": "b9f1a342-7c5e-4d8a-9e2b-1234567890ab",
17 "entity_name": "cart-service",
18 "entity_namespace": "payments",
19 "weight_percentage": 100.0,
20 "source": "user_manual",
21 "assigned_by_user_id": "u-12345",
22 "created_at": "2026-05-07T09:14:00Z",
23 "updated_at": "2026-05-07T09:14:00Z"
24 }
25 },
26 {
27 "id": "8d0e2a4c-3f1b-4593-a715-2c4e6a8b0d1f",
28 "kind": "TeamAssignment",
29 "metadata": {
30 "team": {
31 "id": "7a3b9c12-4d8e-4f01-bc2a-9e8f7a6b5c4d",
32 "name": "Payments"
33 },
34 "cluster": {
35 "id": "c1a2b3c4-d5e6-7890-abcd-ef1234567890",
36 "name": "prod-eu-west-1"
37 },
38 "entity_type": "namespace",
39 "entity_identifier": "checkout",
40 "entity_name": "checkout",
41 "entity_namespace": "checkout",
42 "weight_percentage": 60.0,
43 "source": "k8s_label",
44 "assigned_by_user_id": null,
45 "created_at": "2026-04-22T11:30:00Z",
46 "updated_at": "2026-05-18T03:11:00Z"
47 }
48 }
49 ],
50 "meta": {
51 "request_id": "req_01J5K3V0Q7Y4XR8A2B3C5D7T04",
52 "applied_at": "2026-05-21T10:30:00Z",
53 "pagination": {
54 "next_cursor": "",
55 "has_more": false,
56 "limit": 50
57 }
58 }
59}The first row is a 100% workload assignment created by a human in the Assignment Workbench. The second row is a 60% namespace assignment created automatically by the label reconciler (because the checkout namespace carries kubeadapt.io/team=Payments,Platform and the parsed weight for Payments is 60). The remaining 40% of the checkout namespace cost is owned by another team and appears as a separate row in that team's assignments.
Common errors
| HTTP | error.code | When |
|---|---|---|
| 400 | BAD_REQUEST | team_id is not a valid UUID, malformed limit, malformed cursor. |
| 400 | INVALID_CLUSTER_ID | A cluster_id value in the filter is not a valid UUID. |
| 401 | UNAUTHORIZED | Missing or invalid Bearer token. |
| 403 | FORBIDDEN | Token lacks teams:read. |
| 404 | TEAM_NOT_FOUND | No team with this ID exists in the tenant. |
| 410 | CURSOR_EXPIRED | Cursor older than 24h or query parameters changed. |
| 429 | RATE_LIMITED | Per-key quota exceeded. |
Field notes
originiskubeadaptfor teams created in the UI/API,k8sfor teams mirrored from a configured Kubernetes label key (Settings → Label Keys). Origins are not strictly validated; treat unknown values askubeadaptfor routing.assigned_workloadsandassigned_pvscount distinct entities assigned to the team regardless of weight. A workload split 60/40 across two teams counts as 1 toward both teams.metadata.departmentis omitted entirely (notnull) when a team has no department. The team's cost then does not roll up. See Departments.last_seen_atis the latest cost-data heartbeat across any of the team's entities. A team whose workloads have all been deleted reportslast_seen_atfrom the last day any of them ran.- On assignments,
entity_identifieris the k8smetadata.uidwhenentity_type: workloadand the namespace name whenentity_type: namespace.entity_namespacecarries the namespace name in both cases.
Filtering patterns
1GET /v1/teams?cost_mode=fully_loaded # top spenders, invoice view
2GET /v1/teams?cost_mode=workload_only # team footprint, no overhead
3GET /v1/teams?department_id={dept_id} # teams under a department
4GET /v1/teams?origin=k8s # mirrored from labels
5GET /v1/teams/{team_id}/assignments?cluster_id={id} # team ownership in one cluster
6GET /v1/teams/{team_id}/assignments?entity_type=namespace # namespace-level assignments
7GET /v1/teams/{team_id}/assignments?source=user_manual # Assignment Workbench audit trailSee also
- Departments: the layer above teams. A department aggregates its teams' costs.
- Cost Modes. Fully Loaded vs Workload Only, with worked examples.
- Cost Attribution: Overview: the model behind teams, departments, and assignments.
- Assignment Workbench: the UI behind
source=user_manual. - Label-based attribution: the reconciler behind
source=k8s_label. - Workloads: the resource referenced by
entity_type=workloadassignments. - Cost Explorer: chargeback queries grouped by workload, namespace, or any other dimension, scoped to the clusters your key can see.