Skip to content

API Reference

Teams

List teams, fetch a single team's snapshot, and enumerate the workload and namespace assignments a team owns.

Last updated


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 cost.current_run_rate_hourly is computed at the requested mode. The assignments endpoint carries no cost: ?cost_mode= or ?basis= there returns 422. 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 is the hourly run rate of the team's assigned workloads over the last seven complete days (UTC), weighted by assignment percentage, at the requested cost_mode. It is not a month-to-date figure.

Query parameters

NameTypeDefaultAllowed valuesDescription
limitinteger1001..500Page size.
cursorstringOpaque pagination token from a previous response.
include_totalbooleanfalsetrue, falseInclude meta.pagination.total_count.
sortstringcostcost, idcost puts the highest run rate first. id gives an order that holds while you walk every page. See Sort policy.
cost_modestringfully_loadedfully_loaded, workload_onlyCost attribution mode. See Cost Modes.
department_idstring (csv)UUID listRestrict to teams in specific departments. Teams without a department are excluded when this filter is set.
originstring (csv)Common: k8s, kubeadaptRestrict 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

bash
curl -H "Authorization: Bearer ka_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  "https://public-api.kubeadapt.io/v1/teams?cost_mode=fully_loaded&limit=50"

Example response

json
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": "0196f1c2-a8f1-7f3e-8d27-bde637d00e94",
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

json
1{
2  "data": null,
3  "meta": {
4    "request_id": "0196f1c2-485e-7bfb-ae2c-d85f6bb4fac1",
5    "applied_at": "2026-05-21T10:30:05Z"
6  },
7  "error": {
8    "code": "TEAM_NOT_FOUND",
9    "message": "team not found"
10  }
11}

Common errors

HTTPerror.codeWhen
400BAD_REQUESTMalformed limit, malformed cursor, malformed department_id UUID.
401UNAUTHORIZEDMissing or invalid Bearer token. See Authentication.
403FORBIDDENToken lacks teams:read. See Permission Scopes.
410CURSOR_EXPIREDCursor older than 24h or query parameters changed since issue.
422INVALID_COST_MODEcost_mode value not in fully_loaded, workload_only.
422VALIDATION_ERRORsort or basis value not accepted.
429RATE_LIMITEDPer-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

NameTypeDescription
team_idUUIDTeam identifier returned by the list endpoint.

Query parameters

NameTypeDefaultAllowed valuesDescription
cost_modestringfully_loadedfully_loaded, workload_onlyCost attribution mode. See Cost Modes.

Example request

bash
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). workload_only leaves out the team's share of idle capacity and cluster overhead, so it returns a lower current_run_rate_hourly than fully_loaded. See Cost Modes.

json
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.1800", "currency": "USD" }, "cost_mode": "workload_only" }
9  },
10  "meta": {
11    "request_id": "0196f1c2-0c53-7be0-a980-a7062597b601",
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

HTTPerror.codeWhen
400BAD_REQUESTteam_id is not a valid UUID.
401UNAUTHORIZEDMissing or invalid Bearer token.
403FORBIDDENToken lacks teams:read.
404TEAM_NOT_FOUNDNo team with this ID exists in the tenant.
422INVALID_COST_MODEcost_mode value not in fully_loaded, workload_only.
429RATE_LIMITEDPer-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= (422 INVALID_COST_MODE) or ?basis= (422 VALIDATION_ERROR).

Path parameters

NameTypeDescription
team_idUUIDTeam identifier returned by the list endpoint.

Query parameters

NameTypeDefaultAllowed valuesDescription
limitinteger1001..500Page size.
cursorstringOpaque pagination token from a previous response.
entity_typestring (csv)workload, namespace, pvRestrict by entity kind. Any other value returns 422 VALIDATION_ERROR with the allowed values in details[0].allowed.
cluster_idstring (csv)UUID listRestrict by cluster. Useful for chargeback reports scoped to one production cluster.
sourcestring (csv)Common: user_manual, k8s_label, namespace_auto, backfill_v1Restrict 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

bash
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

json
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": "0196f1c2-065b-7a04-adb5-1fffac93cff8",
52    "applied_at": "2026-05-21T10:30:00Z",
53    "pagination": {
54      "has_more": false,
55      "limit": 50
56    }
57  }
58}

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

HTTPerror.codeWhen
400BAD_REQUESTteam_id is not a valid UUID, malformed limit, malformed cursor.
400INVALID_CLUSTER_IDA cluster_id value in the filter is not a valid UUID.
401UNAUTHORIZEDMissing or invalid Bearer token.
403FORBIDDENToken lacks teams:read.
404TEAM_NOT_FOUNDNo team with this ID exists in the tenant.
410CURSOR_EXPIREDCursor older than 24h or query parameters changed.
422INVALID_COST_MODEThe request carries cost_mode. Assignments have no cost.
422VALIDATION_ERRORThe request carries basis, or entity_type is not workload, namespace or pv.
429RATE_LIMITEDPer-key quota exceeded.

Field notes

  • origin is kubeadapt for teams created in the UI/API, k8s for teams mirrored from a configured Kubernetes label key (Settings → Label Keys). Origins are not strictly validated; treat unknown values as kubeadapt for routing.
  • assigned_workloads and assigned_pvs count distinct entities assigned to the team regardless of weight. A workload split 60/40 across two teams counts as 1 toward both teams.
  • metadata.department is omitted entirely (not null) when a team has no department. The team's cost then does not roll up. See Departments.
  • last_seen_at is the latest cost-data heartbeat across any of the team's entities. A team whose workloads have all been deleted reports last_seen_at from the last day any of them ran.
  • On assignments, entity_identifier is the k8s metadata.uid when entity_type: workload and the namespace name when entity_type: namespace. entity_namespace carries the namespace name in both cases.

Filtering patterns

plaintext
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 trail

See also