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 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>"}.
Related¶
- Overview · Getting started · Auth
- Catalog · Lifecycle · Node pools · Kubeconfig
- Exposure · Elastic IPs · Firewall
- Errors · Limits