Skip to content

API reference

Customer DKS API only: https://dks-api.apps.door.cloud + paths under /v1/catalog and /v1/public/clusters. This page is the customer API contract: every route, header, status code, and schema is listed below. There is no interactive try-it page or downloadable schema file for the customer API.

Who can do this: member = GET (and kubeconfig). super_admin = every mutation below. See Authentication.

Base URL in examples: https://dks-api.apps.door.cloud.

Headers

Header Required Meaning
Authorization yes Bearer $DOOR_TOKEN
X-Door-Organization for collection reads if you do not pass ?organization= and have no preferred org Org name or id. Query ?organization= wins over this header. Create uses body organization.
Idempotency-Key optional Create, cluster delete, node-pool PATCH. Format: 8–255 chars [A-Za-z0-9_\-:.]. Same key + same body replays.
Idempotency-Replay response true when the server returned a stored success for that key.
X-State-Version response on GET …/state Current state_version. Pass as query since on the next long-poll.
Retry-After response on kubeconfig 202 Seconds to wait before the next GET (same as retry_after_seconds).
Content-Type JSON bodies application/json

Catalog

Method Path Role Body Success Async
GET /v1/catalog member — 200 PublicCatalogResponse no

Query: zone (default abidjan). Unknown zone → 400.

Clusters

Method Path Role Body Success Async
GET /v1/public/clusters member — 200 PublicClusterListResponse no
POST /v1/public/clusters super_admin PublicClusterCreateRequest 201 PublicClusterResponse no (provisioning continues)
GET /v1/public/clusters/{id} member — 200 PublicClusterResponse no
PATCH /v1/public/clusters/{id} super_admin PublicClusterUpdateRequest 200 PublicClusterResponse no
PATCH /v1/public/clusters/{id}/exposure super_admin PublicClusterExposurePatchRequest 202 PublicClusterResponse yes
DELETE /v1/public/clusters/{id} super_admin — 202 PublicClusterResponse yes

List query: zone, page (≥1), size (1–100, default 20), include_deleted (bool). Delete query: force (bool) — tear down Door hub environments first.

Create aliases: exposureMode / exposure_mode, networkMode / network_mode.

State

Method Path Role Body Success Async
GET /v1/public/clusters/{id}/state member — 200 PublicClusterStatusResponse long-poll

Query: since (≥0, default 0), timeout_seconds (0–60, default 25). Header X-State-Version. 404 if no snapshot yet.

Kubeconfig

Method Path Role Body Success Async
GET /v1/public/clusters/{id}/kubeconfig member — 200 YAML or PublicKubeconfigResponse; 202 PublicKubeconfigPendingResponse yes when pending

Query: format=plain (default, file download) or format=json. 409 unless phase is ControlPlaneReady or Provisioned.

Node pools

Method Path Role Body Success Async
GET /v1/public/clusters/{id}/node-pools member — 200 PublicNodePoolListResponse no
POST /v1/public/clusters/{id}/node-pools super_admin PublicNodePoolCreateRequest 202 PublicClusterResponse yes
PATCH /v1/public/clusters/{id}/node-pools/{pool} super_admin PublicNodePoolPatchRequest 202 PublicClusterResponse yes
DELETE /v1/public/clusters/{id}/node-pools/{pool} super_admin — 202 PublicClusterResponse yes

Add/scale/delete require Provisioned (409 otherwise). size is not on PATCH.

Nodes

Method Path Role Body Success Async
GET /v1/public/clusters/{id}/nodes member — 200 PublicNodeListResponse no (best-known snapshot)
GET /v1/public/clusters/{id}/node-pools/{pool}/nodes member — 200 PublicNodeListResponse no
DELETE /v1/public/clusters/{id}/nodes/{node_name} super_admin — 202 PublicNodeDeletePendingResponse yes

GET nodes: 409 if not Provisioned; 503 if inventory unavailable. Always 200 with possibly empty items when allowed (cold / stale flags).

Elastic IPs

Method Path Role Body Success Async
GET /v1/public/clusters/{id}/elastic-ips member — 200 PublicElasticIpListResponse no
POST /v1/public/clusters/{id}/elastic-ips super_admin PublicElasticIpCreateRequest 202 PublicElasticIpResponse yes
GET /v1/public/clusters/{id}/elastic-ips/{eip} member — 200 PublicElasticIpResponse no
DELETE /v1/public/clusters/{id}/elastic-ips/{eip} super_admin — 202 PublicElasticIpResponse yes

No public bind/unbind. See Elastic IPs.

Firewall rules

Method Path Role Body Success Async
GET /v1/public/clusters/{id}/firewall-rules member — 200 PublicFirewallRuleListResponse no
POST /v1/public/clusters/{id}/firewall-rules super_admin FirewallRuleCreateRequest 202 FirewallRuleResponse yes
GET /v1/public/clusters/{id}/firewall-rules/{rule} member — 200 PublicFirewallRuleResponse no
DELETE /v1/public/clusters/{id}/firewall-rules/{rule} super_admin — 202 FirewallRuleResponse yes

POST/DELETE items include cluster_id; GET items do not. No phase field.

There is no firewall editor in the console and Maya cannot create rules. Example, office network to HTTPS:

curl -sS -X POST \
  "https://dks-api.apps.door.cloud/v1/public/clusters/${CLUSTER_ID}/firewall-rules" \
  -H "Authorization: Bearer $DOOR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "office-https",
    "protocol": "tcp",
    "port_min": 443,
    "cidrs": ["203.0.113.0/24"]
  }'

Success is 202. port_max defaults to port_min. ICMP echo uses "protocol": "icmp" and omits the ports (type 8, code 0).


Errors

Error body shape

Most 4xx/5xx responses look like:

{ "detail": "cluster 3f9c1a2e-7b4d-4c58-9e21-5d6f8a0b1c2d not found" }

detail is a string for handler-raised errors.

422 validation uses an array:

{
  "detail": [
    {
      "loc": ["body", "name"],
      "msg": "String should match pattern …",
      "type": "string_pattern_mismatch"
    }
  ]
}

HTTP status codes (public API)

These are the codes /v1/public/clusters… and /v1/catalog return.

400 Bad Request

Typical detail What to do
Unknown machine tier: size is not a known machine tier (available machine tiers: …) Use a tier id from the catalog (dks.c5.xlarge, not an internal size name).
Unknown Kubernetes version Use a kubernetes_versions[].id from the catalog (Abidjan: v1.32.4).
unknown zone='…' Use abidjan (or another zone the catalog documents).
Idempotency-Key must match '^[A-Za-z0-9_\\-:.]{8,255}$' Key 8–255 characters: letters, digits, _, -, :, ..
no organization specified and user has no preferred organization Send X-Door-Organization or ?organization= or set a preferred org on your Door account.
Empty node pools / other create validation Send at least one pool; see limits.
Exposure cannot change (not Provisioned, deleting, public disabled, invalid mode) Wait until Provisioned, or use private/public only.
Firewall protocol / port / CIDR Fix the rule; IPv4 only, ≤16 CIDRs.
Invalid node name on delete Node name must look like a DNS hostname (see Nodes tab / kubectl get nodes).

401 Unauthorized

detail What to do
Authorization header missing Send Authorization: Bearer $DOOR_TOKEN.
Authorization scheme must be Bearer Use Bearer, not another scheme.
Bearer token is empty Non-empty token.
invalid bearer token Sign in again; tokens expire. See Authentication.

402 Payment Required

Prepaid organizations only (enterprise invoice orgs skip this gate).

detail What to do
Your organization does not have enough credit balance to cover remaining cluster costs plus one month for the new cluster. Please top up your credits to create a cluster. Top up, or contact your Door account team, then retry create.
Your organization does not have enough credit balance to cover remaining cluster costs after this scale. Please top up your credits. Same, before adding or scaling pools.

403 Forbidden

You are authenticated but not allowed.

Typical case What to do
member on a mutation Ask a super_admin.
Not a member of the cluster's organization Switch organization or request access.
user … has no super_admin role on any organization Pre-check on some writes; you need super_admin somewhere, then on this org.

List/get as member is allowed. Kubeconfig download is allowed for member unless your operator has tightened that setting.

404 Not Found

Typical detail What to do
cluster {id} not found Wrong id, or the cluster is fully gone after delete. Treat 404 after delete as success.
Organization not found Check the org name/id.
Elastic IP / firewall rule / node pool not found Refresh the list. After EIP release, 404 on GET is success.
no projected state for cluster {id} /state has nothing yet; retry shortly after create.

A cluster that exists in another organization typically returns 403, not 404. The list endpoint is org-scoped and will not show it.

409 Conflict

Typical case What to do
Cluster name already exists Pick another name (names are immutable).
Idempotency-Key '…' was previously used with a different payload New key, or replay the same body.
Cluster not Provisioned (pools, EIP, firewall, node list: cluster not ready yet or phase message) Wait for Provisioned.
Exposure: previous change still being applied Retry the same PATCH shortly.
Node-pool op already in flight; last pool; duplicate pool name Wait, or keep at least one pool.
EIP name taken; EIP not Ready/Failed on delete; exposure vs network_mode mismatch Fix name, wait, or create the cluster with the matching network mode.
Firewall duplicate name Another name.
Delete: cluster still has Door hub environments Delete those environments, or DELETE …?force=true.
PATCH metadata on a cluster already deleting Stop; the cluster is going away.
Kubeconfig while not ControlPlaneReady or Provisioned Wait. Message may still say "only available when phase=Provisioned".

422 Unprocessable Content

Schema or merged pool bounds: extra fields (extra="forbid"), missing required fields, count/auto_scale/min_nodes/max_nodes combination, empty PATCH (at least one of: tags, description or pool fields), count=0 without autoscaling.

Note

The public cluster and catalog routes do not return 429 Too Many Requests. There is no rate-limit code for your client to handle.

502 Bad Gateway

Auth or Door hub checks failed (for example hub environments could not be torn down on ?force=true). Retry shortly. If it persists, contact Door support with the cluster id.

503 Service Unavailable

Typical case What to do
Zone cannot accept the create (capacity) Retry later or contact your account team.
Elastic IP product / public pool disabled or exhausted Use private on an intranet cluster, or contact your account team.
Node inventory temporarily unavailable Retry GET nodes.
Kubeconfig download not configured / server URL not ready Retry; if exposure is public, wait until public_exposure_fqdn is set.
Delete: cannot verify Door hub environments Retry without assuming the cluster was deleted.
Catalog missing for the cluster's zone Contact Door support (platform misconfiguration).

500 Internal Server Error

Unexpected server failure (including a kubeconfig that cannot be decrypted or rewritten). Retry once, then contact support with the cluster id. Do not send tokens.

Kubeconfig returns 202 in a loop

202 means a fetch was queued. Response includes retry_after_seconds (platform default 5) and header Retry-After. Call the same GET again after that delay. Need ControlPlaneReady or Provisioned (409 otherwise). If 202 continues for many minutes after Provisioned, contact support with the cluster id.

Exposure PATCH 400 vs 409

Not Provisioned → 400. Another change still applying → 409, retry.


Schema appendix

Field names match JSON. Create/exposure also accept camelCase aliases where noted. additionalProperties: false on these models.

PublicCatalogResponse

schema_version string (catalog.public.v1). zone PublicZoneResponse. kubernetes_versions[] PublicKubernetesVersionResponse. control_plane_options[] PublicControlPlaneOptionResponse (informational; do not send on create). active_control_plane_option_id string | null. machine_tiers[] PublicMachineTierResponse. constraints PublicCatalogConstraintsResponse. defaults PublicCatalogDefaultsResponse. copy object of strings. pricing PublicZonePricingResponse | null.

PublicZoneResponse: id, display_name, region_label (string | null).

PublicKubernetesVersionResponse: id, label, recommended bool, support_channel string | null.

PublicControlPlaneOptionResponse: id, display_name, description, recommended. Informational only — you do not send a control-plane choice on create. Ignore any extra fields on this object.

PublicMachineTierResponse: id, display_name, description | null, spec (vcpu, memory_gib, disk_gib ints), recommended bool.

PublicClusterNameConstraintResponse: pattern, min_length, max_length.

PublicNodePoolsConstraintResponse: min, max, min_nodes_per_pool, max_nodes_per_pool.

PublicCatalogDefaultsResponse: network_mode intranet | internet, exposure_mode public | private.

PublicZonePricingResponse: zone_id, label, currency, hours_per_day | null, hours_per_month | null, items[] (id, label, unit, unit_price, unit_price_day | null, unit_price_month | null).

PublicClusterCreateRequest

Required: name, organization, zone, kubernetes_version, node_pools (1–20 PublicNodePoolRequest). Optional: exposureMode / exposure_mode, networkMode / network_mode, description (max 16384), tags (max 64 DNS labels).

PublicNodePoolRequest: name (default default), size (tier id), count (default 1, 0–200), auto_scale (default false), min_nodes, max_nodes, labels object, tags object.

PublicClusterUpdateRequest

Optional: tags (replaces array, max 64), description (null clears). At least one key. No name.

PublicClusterExposurePatchRequest

Required: exposureMode / exposure_mode = public | private.

PublicClusterResponse

Field Type Description
schema_version string cluster.public.v1
id string Cluster UUID
name string DNS name
zone string e.g. abidjan
kubernetes_version string e.g. v1.32.4
phase string Public lifecycle
state_version int | null Cursor for /state
progress PublicProgress Narration
health ok | degraded Collapsed health
failure_message string | null Customer copy when failed
hub_registration HubRegistrationResponse Door hub registration
default_environment DefaultEnvironmentResponse Default Door environment
node_pools PublicNodePoolResponse[] Pools
total_vcpu int | null Summed capacity
total_memory_gib int | null Summed capacity
total_volume_gib int Summed disk
billable_public_ip_count int Internet Elastic IPs (billing)
exposure_mode public | private API reachability
network_mode intranet | internet App Elastic IP kind
public_exposure_fqdn string | null Hostname when public
private_api_server_endpoint string | null HTTPS URL when private
tags string[] Customer tags
description string | null Notes
created_at / updated_at datetime
deleted_at datetime | null Set while deleting

PublicClusterListResponse: items[], total, page, size, total_pages.

HubRegistrationResponse: status pending | registered | failed, registered_at | null, reason | null (sanitized).

DefaultEnvironmentResponse: status pending | ready | failed, ready_at | null, reason | null.

PublicProgress: lifecycle, step (PublicProgressStep | null), title, message, percent 0–100 | null, health ok | degraded.

PublicProgressStep: index ≥1, total ≥1 (5 on create), label.

PublicClusterStatusResponse

schema_version, cluster_id, state_version, phase, progress, health, operations[] (PublicOperationResponse: kind, phase, progress_pct | null, started_at | null, completed_at | null).

PublicNodePoolResponse

id, name, size (tier id), count, auto_scale, min_nodes | null, max_nodes | null, labels, tags, root_volume_size_gib, lifecycle active | deleting.

PublicNodePoolListResponse: schema_version (nodepool.public.v1), cluster_id, items[], total.

PublicNodePoolCreateRequest: name required, plus the same sizing fields as PublicNodePoolRequest (no default name).

PublicNodePoolPatchRequest: optional count, auto_scale, min_nodes, max_nodes, labels, tags. No size.

PublicNodeResponse

name, pool | null, status (Pending, Provisioning, Provisioned, Running, Deleting, Deleted, Failed, Unknown), kubernetes_version | null, ready bool, created_at | null, deleted_at | null.

PublicNodeListResponse: schema_version (node.public.v1), cluster_id, items[], total, cold, stale, next_refresh_at | null.

PublicNodeDeletePendingResponse: schema_version (node.public.v1), cluster_id, node_name.

PublicElasticIpCreateRequest

Optional name, exposure (private | public | null).

PublicElasticIpResponse

schema_version (elastic_ip.public.v1), id, cluster_id, name | null, exposure, address, status, created_at, updated_at, deleted_at | null.

PublicElasticIpListResponse: schema_version, cluster_id, items[], total.

FirewallRuleCreateRequest

Required protocol. Optional port_min, port_max, cidrs, name, description.

PublicFirewallRuleResponse

id, name | null, protocol, port_min, port_max, cidrs[], description | null, created_at, updated_at, deleted_at | null.

PublicFirewallRuleListResponse: schema_version (firewall_rule.public.v1), cluster_id, items[], total.

FirewallRuleResponse (POST/DELETE): same fields plus cluster_id.

PublicKubeconfigResponse

schema_version (kubeconfig.public.v1), cluster_id, cluster_name, kubeconfig (YAML string), plaintext_size, fetched_at, received_at.

PublicKubeconfigPendingResponse: schema_version, cluster_id, message (kubeconfig fetch requested), retry_after_seconds.

HTTPValidationError (422)

detail: array of { loc, msg, type, … }. Handler errors use {"detail": "<string>"}.