Clusters (v2)

Cluster lifecycle and hub-aware operations under /api/v1/clustersv2. All paths on this page are relative to /api/v1; the full path for the first endpoint, for example, is /api/v1/clustersv2/providers/{provider}/skip-credentials.

Common conventions:

  • owner — the organization slug or username that owns the cluster (e.g. appscode).
  • cluster — the cluster name within that owner’s scope (e.g. ace, arnob-dev, arnob-monitoring).
  • Auth — every endpoint requires a token unless noted; send it as Authorization: token <YOUR_TOKEN> (or a token / access_token query parameter).
  • K8s object responses — some endpoints return an arbitrary Kubernetes object or list (for example an UnstructuredList of clusterprofiles, a ConfigMapList, or a cluster status). These are passed through verbatim from the member cluster and are documented as opaque objects rather than fixed schemas.

Example request:

curl -H "Authorization: token $AKP_TOKEN" \
  https://<akp-host>/api/v1/clustersv2/appscode/ace/status

Provider & hub discovery

GET /clustersv2/providers/{provider}/skip-credentials

Check whether manual cloud credentials can be skipped for a provider. Only EKS is supported; it verifies the current environment has the AWS IAM permissions needed to skip manual credentials.

Auth: token. Requires site-admin privileges (admin_of_administrative_org:site_admin).

Path parameters:

NameTypeDescription
providerstringCloud provider identifier. Only eks is supported.

Response: 200 when credentials can be skipped, 400 when they cannot (unsupported provider or insufficient permissions). Both return a SkipCredentialsResponse:

{
  "skipCredentials": true,
  "reason": "",
  "message": "IAM permissions verified"
}
FieldTypeDescription
skipCredentialsbooleanWhether credentials can be skipped.
reasonstringMachine-readable reason (when not skippable).
messagestringHuman-readable detail.

Verified: GET returned 400 for providers/eks/skip-credentials on 2026-07-14 — this KubeDB Platform instance is not an EKS environment with the required IAM permissions, which is the expected non-skippable outcome.


GET /clustersv2/all-hubs

List all hub clusters owned by the user across their organizations.

Auth: token.

Response: 200 — an array of HubCluster. 400 if the user owns no organization.

[
  {
    "name": "ace",
    "ownerName": "appscode",
    "displayName": "ace",
    "hubClusterUID": "<uid>",
    "spokeClusters": 0,
    "addOns": 0,
    "clusterNode": 1,
    "provider": "Generic",
    "hubType": "OCM",
    "locations": "",
    "age": "43m",
    "createdAt": "2026-07-05T06:49:31Z"
  }
]
FieldTypeDescription
namestringHub cluster name.
ownerNamestringOwner (org/user) name.
displayNamestringDisplay name.
hubClusterUIDstringHub cluster UID.
spokeClustersintegerNumber of spoke clusters.
addOnsintegerNumber of add-ons installed.
clusterNodeintegerNode count.
providerstringCloud/infra provider.
hubTypestringHub type (e.g. OCM).
locationsstringLocations summary.
agestringHuman-readable age.
createdAtstring (date-time)Creation timestamp.

Verified: GET returned 200 against appscode on 2026-07-14 (one hub, ace).


GET /clustersv2/{owner}/hub-info

Get hub cluster connection info (API server and token) for the owner’s OCM hub clusters.

Auth: token.

Path parameters:

NameTypeDescription
ownerstringOrganization slug or username.

Response: 200 — a map keyed by cluster name; each value contains apiServer and a bearer token. 400 if listing OCM manager clusters fails.

{
  "ace": {
    "apiServer": "https://<akp-host>:6443",
    "token": "<bearer-token>"
  }
}

Note: the token value is a live service-account bearer token — treat it as a secret.

Verified: GET returned 200 against appscode (hub ace) on 2026-07-14.


GET /clustersv2/{owner}/has-hub

Check whether the owner has any hub cluster.

Auth: token.

Path parameters:

NameTypeDescription
ownerstringOrganization slug or username.

Response: 200{ "has-hub": <bool> }. 400 if listing OCM manager clusters fails.

{ "has-hub": true }

Verified: GET returned 200 against appscode on 2026-07-14 ({"has-hub":true}).


GET /clustersv2/cluster-profiles

List available cluster profiles.

Auth: token.

Response: 200 — an UnstructuredList of clusterprofiles (meta.k8s.appscode.com/v1alpha1). This is an arbitrary Kubernetes list object; see the cluster features guide for how profiles are used.

Verified: GET returned 200 against appscode on 2026-07-14.


GET /clustersv2/identity/{clusterID}

Get cluster identity metadata by cluster UID.

Auth: token. (Used for cross-cluster identity resolution; intended for platform/admin flows.)

Path parameters:

NameTypeDescription
clusterIDstringCluster UID.

Response: 200 — a ClusterMetadata object. 404 if the cluster info is not found.

{
  "uid": "<uid>",
  "name": "ace",
  "displayName": "ace",
  "provider": "Generic",
  "ownerID": "3",
  "ownerType": "org",
  "apiEndpoint": "https://<akp-host>:6443",
  "caBundle": "<base64-ca>",
  "managerID": "",
  "hubClusterID": "<uid>",
  "cloudServiceAuthMode": "",
  "mode": "prod"
}
FieldTypeDescription
uidstringCluster UID.
namestringCluster name.
displayNamestringDisplay name.
providerstringProvider.
ownerIDstringOwner ID.
ownerTypestringOwner type (org/user).
apiEndpointstringKubernetes API server endpoint.
caBundlestringBase64-encoded CA bundle.
managerIDstringManaging cluster ID.
hubClusterIDstringHub cluster UID.
cloudServiceAuthModestringCloud service auth mode.
modestringOne of prod, qa, staging, dev.

Verified: GET returned 404 for the hub cluster UID on 2026-07-14 — the identity registry lookup is keyed differently from the imported-cluster list, so this UID is not resolvable here on this instance (the route itself is reachable and authorized).


Listing & status

GET /clustersv2/{owner}/all-clusters

List all imported clusters for an owner.

Auth: token. Requires site-admin privileges (view_orgs:site_admin).

Path parameters:

NameTypeDescription
ownerstringOrganization slug or username.

Response: 200 — an array of ClusterInfo (see the schema under GET /clustersv2/{owner}).

Verified: GET returned 200 against appscode on 2026-07-14.


GET /clustersv2/{owner}

List clusters for an owner (each with a resolved provider).

Auth: token.

Path parameters:

NameTypeDescription
ownerstringOrganization slug or username.

Response: 200 — an array of ClusterInfo:

[
  {
    "id": 2,
    "displayName": "ace",
    "name": "ace",
    "uid": "<uid>",
    "ownerID": 3,
    "ownerName": "appscode",
    "provider": "Generic",
    "vendor": "",
    "infraNamespace": "",
    "isMonitoringCluster": false,
    "endpoint": "https://<akp-host>:6443",
    "kubernetesVersion": "v1.36.2+k3s1",
    "nodeCount": 1,
    "createdAt": "2026-07-05T06:49:31Z",
    "age": "43m",
    "clusterManagers": ["ACE", "OCMHub"],
    "isCapiClusterUpgradeable": false,
    "status": { "response": { "phase": "Registered" } }
  }
]
FieldTypeDescription
idinteger (int64)Internal cluster record ID.
displayNamestringDisplay name.
namestringCluster name.
uidstringCluster UID.
ownerIDinteger (int64)Owner ID.
managerIDinteger (int64)Managing cluster ID.
externalIDstringExternal (provider) ID.
hubClusterName / hubClusterUID / hubClusterOwnerNamestringAssociated hub cluster.
clusterSetNamestringCluster-set name.
ownerNamestringOwner (org/user) name.
providerstringProvider (e.g. Generic, eks).
vendorstringKubernetes vendor.
infraNamespacestringInfrastructure namespace.
isMonitoringClusterbooleanWhether this is a monitoring cluster.
endpointstringKubernetes API endpoint.
location / projectstringLocation / project.
kubernetesVersionstringKubernetes version.
nodeCountinteger (int32)Node count.
createdAtstring (date-time)Creation time.
agestringHuman-readable age.
clusterManagersstring[]Managers (e.g. ACE, OCMHub).
isCapiClusterUpgradeablebooleanWhether a CAPI upgrade is available.
statusobjectKubernetes-style ClusterStatus (opaque).

Verified: GET returned 200 against appscode on 2026-07-14.


GET /clustersv2/{owner}/{cluster}/exist

Check whether the cluster is imported for the given owner.

Auth: token.

Path parameters:

NameTypeDescription
ownerstringOrganization slug or username.
clusterstringCluster name.

Response: 200{ "exist": <bool> }.

{ "exist": true }

Verified: GET returned 200 against appscode/arnob-dev on 2026-07-14 ({"exist":true}).


GET /clustersv2/{owner}/{cluster}/status

Get the current status of a cluster.

Auth: token.

Path parameters:

NameTypeDescription
ownerstringOrganization slug or username.
clusterstringCluster name.

Response: 200 — a ClusterInfo with its status field resolved (same schema as GET /clustersv2/{owner}).

Verified: GET returned 200 against appscode/ace (hub) on 2026-07-14; also 200 against appscode/arnob-monitoring (spoke).


Lifecycle & reconciliation

GET /clustersv2/{owner}/{cluster}/reconcile

Reconcile Helm releases for the specified features. Adds reconcile annotations to the HelmRelease objects of the features passed in the features query parameter.

Auth: token.

Path parameters:

NameTypeDescription
ownerstringOrganization slug or username.
clusterstringCluster name.

Query parameters:

NameTypeRequiredDescription
featuresstringNoComma-separated list of feature names to reconcile.

Response: 200 when reconcile is triggered. 400 if a feature or its HelmRelease is not found.

Verified: GET returned 500 against appscode/ace on 2026-07-14 when called with no features argument — the handler requires valid feature names to resolve HelmRelease objects.


POST /clustersv2/{owner}/check

Check whether a cluster already exists / is importable.

Auth: token.

Path parameters:

NameTypeDescription
ownerstringOrganization slug or username.

Request body: a CheckOptions object.

{
  "basicInfo": {
    "name": "my-cluster",
    "displayName": "My Cluster",
    "clusterUID": "<uid>"
  },
  "provider": {
    "name": "eks",
    "credential": "aws-cred",
    "region": "us-east-1",
    "clusterID": "my-eks"
  }
}
FieldTypeRequiredDescription
basicInfoobject (BasicInfo)NoCluster basic info (name, displayName, ownerID, managerID, userID, clusterUID, hubClusterID, infraNamespace).
providerobject (ProviderOptions)NoProvider details (name, credential, eksAuthMode IRSA/PodIdentity, clusterID, project, region, resourceGroup, kubeConfig).

Response: 200 — a ClusterInfo describing existence / import status. 422 for an invalid body.


POST /clustersv2/{owner}/validate

Validate cluster import options.

Auth: token.

Path parameters:

NameTypeDescription
ownerstringOrganization slug or username.

Query parameters:

NameTypeRequiredDescription
operationstringNoSet to connect to skip Flux/feature-set detection.

Request body: an ImportOptions object.

{
  "basicInfo": { "name": "my-cluster", "displayName": "My Cluster" },
  "provider": { "name": "eks", "credential": "aws-cred", "region": "us-east-1" },
  "components": {
    "fluxCD": true,
    "allFeatures": false,
    "clusterProfile": "default",
    "featureSets": [ { "name": "opscenter-core", "features": ["kube-ui-server"] } ]
  }
}
FieldTypeRequiredDescription
basicInfoobject (BasicInfo)NoCluster basic info.
providerobject (ProviderOptions)NoProvider details.
componentsobject (ComponentOptions)NoComponents to install: fluxCD (bool), featureSets (array of {name, features[]}), allFeatures (bool), clusterProfile (string), monitoringClusterName (string), spokeComponent (bool).

Response: 200 — a ValidationResponse:

{
  "alreadyImported": false,
  "displayName": { "valid": true, "reason": "" },
  "clusterId": { "valid": true, "reason": "" },
  "message": "",
  "monitoringClusterList": ["arnob-monitoring"]
}
FieldTypeDescription
alreadyImportedbooleanWhether the cluster is already imported.
displayName / clusterIdobject (FieldValidation)Per-field {valid, reason}.
fluxCD / availableFeatureSets / clusterAPIobjectOpaque Kubernetes objects (detected state).
messagestringValidation message.
clusterMetadataobject (ClusterMetadata)Detected cluster metadata.
monitoringClusterListstring[]Candidate monitoring clusters.

422 for an invalid body.


POST /clustersv2/{owner}/import

Import a cluster.

Auth: token.

Path parameters:

NameTypeDescription
ownerstringOrganization slug or username.

Request body: an ImportOptions object (same schema as /validate).

Response: 200 — the imported ClusterInfo. 422 for an invalid body.


POST /clustersv2/{owner}/{cluster}/connect

Connect (or re-connect) to an already-imported cluster.

Auth: token.

Path parameters:

NameTypeDescription
ownerstringOrganization slug or username.
clusterstringCluster name.

Query parameters:

NameTypeRequiredDescription
operationstringNoSet to re-connect to update existing credentials.

Request body: a ConnectOptions object.

{
  "name": "my-cluster",
  "credential": "aws-cred",
  "kubeConfig": "<kubeconfig-yaml>"
}
FieldTypeRequiredDescription
namestringNoCluster name.
credentialstringNoStored cloud credential name.
kubeConfigstringNoKubeconfig contents.

Response: 200ClusterInfo after connecting. 400 if connecting fails (e.g. existing user auth on a non-reconnect), 422 for an invalid body.


POST /clustersv2/{owner}/{cluster}/remove

Remove a cluster and its components.

Auth: token.

Path parameters:

NameTypeDescription
ownerstringOrganization slug or username.
clusterstringCluster name.

Query parameters:

NameTypeRequiredDescription
response-idstringNoOptional response identifier for progress notifications.
notifybooleanNoWhether to send progress notifications (defaults to true).

Request body: a RemovalOptions object.

{
  "name": "my-cluster",
  "components": { "fluxCD": true, "allFeatures": true }
}
FieldTypeRequiredDescription
namestringNoCluster name.
componentsobject (ComponentOptions)NoComponents to remove.

Response: 200 when the removal task is submitted. 422 for an invalid body.


POST /clustersv2/{owner}/{cluster}/reconfigure

Reconfigure a cluster’s components.

Auth: token.

Path parameters:

NameTypeDescription
ownerstringOrganization slug or username.
clusterstringCluster name.

Request body: a ReconfigureOptions object.

{
  "basicInfo": { "name": "my-cluster" },
  "components": {
    "allFeatures": false,
    "featureSets": [ { "name": "opscenter-core", "features": ["kube-ui-server"] } ]
  }
}
FieldTypeRequiredDescription
basicInfoobject (BasicInfo)NoCluster basic info.
componentsobject (ComponentOptions)NoComponents to reconfigure.

Response: 200 — the internal cluster info record (modeled as a generic Kubernetes object). 422 for an invalid body.


POST /clustersv2/{owner}/{cluster}/feature/{feature}/convert

Convert a feature to be UI-managed.

Auth: token.

Path parameters:

NameTypeDescription
ownerstringOrganization slug or username.
clusterstringCluster name.
featurestringName of the feature to convert.

Response: 200 when the feature is converted. 400 if the feature is not enabled or already UI-managed.


Kubernetes version upgrades

GET /clustersv2/{owner}/{cluster}/update-version/operations

List cluster version update operations.

Auth: token.

Path parameters:

NameTypeDescription
ownerstringOrganization slug or username.
clusterstringCluster name.

Response: 200 — a ConfigMapList of upgrade operations (an arbitrary Kubernetes list object).

Verified: GET returned 200 against appscode/ace on 2026-07-14.


GET /clustersv2/{owner}/{cluster}/update-version/list

List upgradeable Kubernetes versions for a cluster.

Auth: token.

Path parameters:

NameTypeDescription
ownerstringOrganization slug or username.
clusterstringCluster name.

Response: 200 — an array of version strings.

["v1.37.0", "v1.38.0"]

Verified: GET returned 500 against appscode/ace on 2026-07-14 — ace is a k3s hub cluster, not a CAPI-managed cluster, so upgradeable-version discovery does not apply. This endpoint targets CAPI clusters.


GET /clustersv2/{owner}/{cluster}/update-version/{version}

Upgrade a CAPI cluster to a target Kubernetes version.

Auth: token.

Path parameters:

NameTypeDescription
ownerstringOrganization slug or username.
clusterstringCluster name.
versionstringTarget Kubernetes version.

Response: 200 when the upgrade task is submitted.

Note: although this is an HTTP GET, it triggers a state change (a CAPI cluster upgrade). It was not exercised against the live platform.


Gateway configurations

GET /clustersv2/{owner}/{cluster}/gateway-configs

List gateway configurations.

Auth: token.

Path parameters:

NameTypeDescription
ownerstringOrganization slug or username.
clusterstringCluster name.

Response: 200 — an array of GatewayConfigInfo:

[
  {
    "presetName": "ace",
    "presetNamespace": "ace",
    "configName": "ace",
    "configNamespace": "ace",
    "version": "v2026.6.19",
    "host": "<akp-host>",
    "hostType": "ip",
    "isDefault": true,
    "updateAvailable": false
  }
]
FieldTypeDescription
presetName / presetNamespacestringGateway preset name and namespace.
configName / configNamespacestringGatewayConfig object name and namespace.
versionstringChart version of the config.
hoststringGateway host (IP or hostname).
hostTypestringHost type (e.g. ip, domain).
isDefaultbooleanWhether this is the default gateway.
updateAvailablebooleanWhether an update to the ace chart version is available.

Verified: GET returned 200 against appscode/ace on 2026-07-14.


PUT /clustersv2/{owner}/{cluster}/gateway-configs

Update all non-ace gateway configurations to the ace chart version.

Auth: token.

Path parameters:

NameTypeDescription
ownerstringOrganization slug or username.
clusterstringCluster name.

Request body: none.

Response: 200 when the gateway configurations are updated.


PUT /clustersv2/{owner}/{cluster}/gateway-configs/{namespace}/{name}

Update a single gateway configuration to the ace chart version.

Auth: token.

Path parameters:

NameTypeDescription
ownerstringOrganization slug or username.
clusterstringCluster name.
namespacestringNamespace of the GatewayConfig.
namestringName of the GatewayConfig.

Request body: none.

Response: 200 when updated. 400 if the target is the ace gateway config (which cannot be updated this way).


Virtual clusters (vcluster)

POST /clustersv2/{owner}/vcluster/create

Create a virtual cluster on a host cluster.

Auth: token.

Path parameters:

NameTypeDescription
ownerstringOrganization slug or username.

Request body: a ClusterOptions object.

{
  "clusterName": "my-vcluster",
  "hostClusterName": "ace",
  "hostClusterUID": "<uid>",
  "hostClusterOwnerID": 3,
  "hubClusterName": "ace",
  "hubClusterUID": "<uid>",
  "hubClusterOwnerID": 3
}
FieldTypeRequiredDescription
clusterNamestringNoName for the new virtual cluster.
hostClusterNamestringNoHost cluster name.
hostClusterUIDstringNoHost cluster UID.
hostClusterOwnerIDinteger (int64)NoHost cluster owner ID.
hubClusterNamestringNoHub cluster name.
hubClusterUIDstringNoHub cluster UID.
hubClusterOwnerIDinteger (int64)NoHub cluster owner ID.

Response: 200 when the creation task is submitted. 422 for an invalid body.


POST /clustersv2/{owner}/vcluster/import

Import a virtual cluster from a host cluster.

Auth: token.

Path parameters:

NameTypeDescription
ownerstringOrganization slug or username.

Request body: a ClusterOptions object (same schema as /vcluster/create).

Response: 200 when the import task is submitted. 422 for an invalid body.