API Reference
Permission Scopes
The nine Kubeadapt API read scopes, the endpoints each one covers, the cluster ACL model, the 403 details payload, and minimum-token recipes.
Every API key encodes one or more of the nine *:read scopes — there are no write, admin, or break-glass scopes. Scopes gate endpoint families; a separate cluster ACL on the same key narrows access to a subset of clusters within those endpoints. Operations that would require write access (creating teams, applying recommendations, rotating cluster credentials) live in the Kubeadapt dashboard, not the API.
The nine read scopes
organization:read
The tenant-level view: total clusters, total spend, month-to-date billed cost, savings potential. The dashboard endpoint is the only place where MTD and savings figures live.
Endpoints:
GET /v1/organization: the snapshot. Aggregate capacity, utilization, and cost across allowed clusters. Rejects?cost_mode=(the org-level bill is one physical number).GET /v1/organization/dashboard: the dashboard. Month-to-date billed cost, savings potential (current hourly potential + recommendation count), top-N clusters by run rate. Accepts?cost_mode=and returns genuinely different numbers per mode.
clusters:read
Cluster inventory: enumerate clusters, look one up by ID, list cluster status.
Endpoints:
GET /v1/clusters: paginated list of clusters in the key's allow-list. Filters:provider,region,environment,status. Default sort bycost.current_run_rate_hourly DESC. Rejects?cost_mode=.GET /v1/clusters/{cluster_id}: single cluster body. Rejects?cost_mode=.
namespaces:read
Kubernetes namespaces with namespace-level cost rollups, optionally filtered by cluster.
Endpoints:
GET /v1/namespaces: paginated cross-cluster list.GET /v1/clusters/{cluster_id}/namespaces: paginated list scoped to one cluster.GET /v1/clusters/{cluster_id}/namespaces/{namespace}: single namespace body.
All three accept ?cost_mode=. The response echoes the requested mode in meta.cost_mode and cost.cost_mode. Both modes currently return the same cost amount for namespace endpoints.
workloads:read
Deployments, StatefulSets, DaemonSets, and the pod sub-list scoped to a workload. The controller-granularity surface used by chargeback pipelines, anomaly detection, and rightsizing dashboards.
Endpoints:
GET /v1/workloads: paginated cross-cluster list. Filter?kind=to one or more ofDeployment,StatefulSet,DaemonSet.GET /v1/clusters/{cluster_id}/workloads: paginated list scoped to one cluster.GET /v1/workloads/{workload_uid}: single workload body.GET /v1/workloads/{workload_uid}/pods: paginated pod list for the workload.
All four accept ?cost_mode=. Same as namespaces: the response echoes the requested mode, but both modes currently return the same cost amount for workload endpoints.
nodes:read
Kubernetes nodes plus the node-group aggregates derived from them. Both families share this scope; there is no separate node-groups:read.
Endpoints:
GET /v1/nodes: paginated cross-cluster list.GET /v1/clusters/{cluster_id}/nodes: paginated list scoped to one cluster.GET /v1/nodes/{node_uid}: single node body.GET /v1/node-groups: cross-cluster node-group aggregates. Not paginated.GET /v1/clusters/{cluster_id}/node-groups: node groups in one cluster. Not paginated.GET /v1/clusters/{cluster_id}/node-groups/{name}: single node-group body.
Every node and node-group endpoint rejects ?cost_mode= with 422 INVALID_COST_MODE. Nodes are physical infrastructure; their cost is one number, not a mode choice.
recommendations:read
Cost and efficiency recommendations with savings projections.
Endpoints:
GET /v1/recommendations: paginated list. Filters:cluster_id,namespace,recommendation_type(public API surfaces onlyworkload_rightsizing),status(pending,applied,dismissed,archived),risk_level(low,medium,high),priority(low,medium,high),resource_type(Deployment,StatefulSet,DaemonSet,Pod,Node),workload_uid,min_savings_hourly.GET /v1/recommendations/{rec_id}: single recommendation body, including ametrics_snapshotfield.
Both endpoints reject ?cost_mode=. Recommendation savings use a fixed pricing model and are mode-agnostic.
teams:read
Team identity and the workload assignments that attach cost to teams. The scope a finance chargeback pipeline requires.
Endpoints:
GET /v1/teams: paginated team list with month-to-date cost rollups.GET /v1/teams/{team_id}: single team body.GET /v1/teams/{team_id}/assignments: paginated list of the workloads attributed to this team.
All three accept ?cost_mode= and return different cost figures per mode.
departments:read
Department identity and rolled-up department cost. Departments sit one level above teams in the attribution hierarchy.
Endpoints:
GET /v1/departments: paginated department list.GET /v1/departments/{dept_id}: single department body, including child team summaries.
Both accept ?cost_mode= and return different cost figures per mode.
cost_explorer:read
Aggregated cost queries with grouping, filtering, and KPI summaries via Cost Explorer.
Endpoints:
POST /v1/cost-explorer/query: paginated cost rows + summary KPIs. The onlyPOSTin/v1. Uses offset pagination (page/per_page) and accepts?cost_mode=.
cost_explorer:read does not imply or substitute for any other scope; if your integration needs the rolled-up cost-explorer view and the per-resource endpoints, mint a key with both.
The scope-to-endpoint table
For copy-paste into your integration's docs:
| Scope | Endpoints |
|---|---|
organization:read | /v1/organization, /v1/organization/dashboard, /v1/organizations/dashboard/cost-trend |
clusters:read | /v1/clusters, /v1/clusters/{id}, /v1/clusters/{id}/cost-trend |
namespaces:read | /v1/namespaces, /v1/clusters/{id}/namespaces, /v1/clusters/{id}/namespaces/{ns}, /v1/clusters/{id}/namespaces/{ns}/cost-trend |
workloads:read | /v1/workloads, /v1/clusters/{id}/workloads, /v1/workloads/{uid}, /v1/workloads/{uid}/pods, /v1/clusters/{id}/workloads/by-uid/{uid}/cost-trend |
nodes:read | /v1/nodes, /v1/clusters/{id}/nodes, /v1/nodes/{uid}, /v1/node-groups, /v1/clusters/{id}/node-groups, /v1/clusters/{id}/node-groups/{name}, /v1/clusters/{id}/node-groups/{name}/cost-trend |
recommendations:read | /v1/recommendations, /v1/recommendations/{id} |
teams:read | /v1/teams, /v1/teams/{id}, /v1/teams/{id}/assignments |
departments:read | /v1/departments, /v1/departments/{id} |
cost_explorer:read | /v1/cost-explorer/query |
A request to an endpoint not covered by any of your key's scopes returns 403 FORBIDDEN with the missing scope in error.details[0].
Cluster ACLs
Scopes are the first axis of authorization. Cluster ACLs are the second.
A key minted with clusters:read and an allow-list of three cluster UUIDs sees only those three clusters: GET /v1/clusters returns the three, GET /v1/clusters/{cluster_id} for a fourth returns 403 CLUSTER_ACCESS_DENIED. The allow-list applies to every cluster-scoped path, regardless of which scope opens it. Asking for a workload in a forbidden cluster, a namespace in a forbidden cluster, or a node in a forbidden cluster all return 403.
The allow-list is tri-state (nil, [], [ids]) — see Authentication § The cluster ACL tri-state for the full table.
Cross-cluster list endpoints (/v1/clusters, /v1/workloads, /v1/namespaces, etc.) return only the clusters the API key can access; they do not return 403 on a mismatch. The 403 path triggers only when an integration asks for a specific cluster by ID that is not permitted.
The 403 details payload
Both 403 shapes are machine-parseable. Customers writing retry logic, UX messaging, or onboarding flows key off error.details[0].
For FORBIDDEN (missing scope):
1{
2 "data": null,
3 "meta": {
4 "request_id": "req_01J5K3V0Q7Y4XR8A2B3C5D7E9F",
5 "applied_at": "2026-05-21T10:30:00Z"
6 },
7 "error": {
8 "code": "FORBIDDEN",
9 "message": "API key is missing the required scope",
10 "details": [{ "required": "clusters:read" }]
11 }
12}For CLUSTER_ACCESS_DENIED (blocked cluster):
1{
2 "data": null,
3 "meta": {
4 "request_id": "req_01J5K3V0Q7Y4XR8A2B3C5D7E9G",
5 "applied_at": "2026-05-21T10:30:01Z"
6 },
7 "error": {
8 "code": "CLUSTER_ACCESS_DENIED",
9 "message": "API key is not allowed to access this cluster",
10 "details": [{ "cluster_id": "c1a2b3c4-d5e6-7890-abcd-ef1234567890" }]
11 }
12}Minimum-token recipes
Three concrete starting points for common integrations. Each picks the smallest scope set that gets the job done.
Read-only dashboard exporter
Pulls cluster, namespace, workload, and node data into your own dashboarding stack (Grafana, Looker, Tableau, Metabase). Reads org-level summary for headline numbers.
Scopes:
organization:readclusters:readnamespaces:readworkloads:readnodes:read
Cluster ACL: leave it at nil (all clusters) for a centralized export. Lock it down by cluster if the exporter is per-region or per-business-unit.
Finance chargeback pipeline
Runs monthly. Pulls cost-by-team and cost-by-department, plus the org-level total for reconciliation. Does not touch infrastructure resources.
Scopes:
teams:readdepartments:readorganization:read
Cluster ACL: nil. Chargeback aggregates across the whole tenant.
SRE savings dashboard
Surfaces optimization opportunities for a platform team. Lists active recommendations, filters by cluster, drills into nodes for the rightsizing details.
Scopes:
recommendations:readclusters:readnodes:read
Cluster ACL: nil for an org-wide view, or scope to the production clusters only if the dashboard is a production runbook.
Anti-patterns
A few patterns to avoid because they create operational pain later:
- One omnibus key with every scope, used by everything. When something goes wrong (leak, compromised integration, key rotation) you have to coordinate across every consumer at once. Mint per integration.
- Scopes a key "might need later". Speculative permissions widen the blast radius of a leak. Mint with the minimum scope set; mint a second key when the additional scope is actually needed.
- Cluster ACL set to
nilbecause filling it in is tedious. If the integration is logically scoped to a subset of clusters (per-BU exporter, per-region pipeline), encode that in the ACL. Saves you when a new cluster joins and you don't want it in the export by default.
See also
- Authentication: the Bearer token format, minting and rotation, the 401 vs 403 distinction.
- API Overview: base URL, envelope shape, rate limits, the resource-family index.
- Error Handling: the full error code catalog including
FORBIDDENandCLUSTER_ACCESS_DENIED.