Descanto Docs
API Reference

Desktops

Create, list, read, update, wake, hibernate, destroy, fork.

List desktops

GET /v1/desktops

ParamInRequiredDescription
statequerynoOptional lifecycle-state filter, e.g. awake
curl -sS "$BASE/desktops?state=awake" -H "$AUTH"
{ "desktops": [ /* DesktopJson[] */ ] }
StatusMeaning
200Desktops belonging to the caller's org
400Invalid state filter
401Missing/invalid credentials

Create a desktop

POST /v1/desktops

ParamInRequiredDescription
Idempotency-KeyheadernoClient-supplied dedup key, scoped per-org. A repeat request with the same key (and org) returns the existing desktop (200) instead of creating a new one (201).

Request body (CreateDesktopBody):

{
  "tier": "small",
  "billing_mode": "hourly",
  "image_version": null,
  "idle_timeout_secs": null,
  "env": null,
  "setup_script": null
}
FieldTypeRequiredDescription
tierstringyesOne of small/default/large.
billing_modestring | nullnomonthly or hourly; defaults to hourly when omitted/empty.
image_versionstring | nullno
idle_timeout_secsinteger | nullnoSeconds of guest idle time (from wake) before auto-hibernate; 0 means never. See Concepts: TTL and auto-hibernate.
envobject | nullnoEnv vars written to /root/.canto/env in the guest on first successful wake. Capped at 100 keys / 64 KiB total serialized size; every key must match ^[A-Za-z_][A-Za-z0-9_]*$ -- 400 past either cap or on any invalid key. Not echoed back by GET/POST.
setup_scriptstring | nullnoFirst-wake setup script, run once under hostd's reserved process_id = "setup". Capped at 64 KiB -- 400 past that. Not echoed back.
curl -sS -X POST "$BASE/desktops" \
  -H "$AUTH" -H 'Content-Type: application/json' \
  -d '{
    "tier": "small",
    "billing_mode": "hourly",
    "env": {"APP_ENV": "staging"},
    "setup_script": "#!/bin/sh\napt-get update && apt-get install -y ripgrep\n"
  }'

See Concepts: env and setup_script for the full charset/cap rules and the /root/.canto/env guest layout.

StatusMeaning
200Idempotency-Key already used by this org -- returns the existing desktop, not a new one
201Desktop created
400Invalid tier/billing_mode, malformed body, or a non-ASCII Idempotency-Key
401Missing/invalid credentials

Both 200 and 201 return a full DesktopJson.

Get a desktop

GET /v1/desktops/{id}

curl -sS "$BASE/desktops/$DESKTOP_ID" -H "$AUTH"
StatusMeaning
200The desktop (DesktopJson)
400Malformed id
401Missing/invalid credentials
404Desktop not found or belongs to another org

Update a desktop

PATCH /v1/desktops/{id}

Today the only updatable field is idle_timeout_secs -- see Concepts: TTL and auto-hibernate.

ParamInRequiredDescription
idpathyesDesktop id

Request body (UpdateDesktopBody, required; an empty/all-omitted body is a no-op):

{ "idle_timeout_secs": 3600 }
FieldTypeRequiredDescription
idle_timeout_secsinteger | nullno0 means "never auto-hibernate." A negative value is 400. Omitting the field is a no-op -- the current desktop is returned unchanged.
curl -sS -X PATCH "$BASE/desktops/$DESKTOP_ID" \
  -H "$AUTH" -H 'Content-Type: application/json' \
  -d '{"idle_timeout_secs": 3600}'
StatusMeaning
200Updated (or unchanged, if the body carried no fields) desktop (DesktopJson)
400Malformed id/body, or a negative idle_timeout_secs
401Missing/invalid credentials
404Desktop not found or belongs to another org

Wake / hibernate / destroy

POST /v1/desktops/{id}/wake POST /v1/desktops/{id}/hibernate POST /v1/desktops/{id}/destroy

These three share an identical request/response shape.

ParamInRequiredDescription
idpathyesDesktop id
waitquerynoLong-poll until the operation settles (2s cadence, 120s cap)
Idempotency-KeyheadernoClient-supplied dedup key, scoped per-desktop

Request body (MutationBody, optional -- an empty body is equivalent to {}):

{ "expected_generation": null }

expected_generation is an optional fencing generation the caller expects the desktop to currently be on -- see Concepts: generation fencing.

curl -sS -X POST "$BASE/desktops/$DESKTOP_ID/wake?wait=true" -H "$AUTH"
curl -sS -X POST "$BASE/desktops/$DESKTOP_ID/hibernate?wait=true" -H "$AUTH"
curl -sS -X POST "$BASE/desktops/$DESKTOP_ID/destroy?wait=true" -H "$AUTH"
StatusMeaning
200Operation settled, or the ?wait=true cap was reached while still pending/running
202Operation enqueued (no ?wait=true)
400Malformed id or body
401Missing/invalid credentials
404Desktop not found or belongs to another org
409Stale expected_generation or wrong desktop state
503This node is not the leader

Both 200 and 202 return an OperationJson -- see API reference: Operations for its shape and Concepts: Operations for the polling model.

Fork a desktop

POST /v1/desktops/{id}/fork

Creates count new, independent desktops as memory-snapshot copies of a Hibernated source desktop. See Concepts: Forking for the full model (copy-on-write semantics, acknowledge_shared_state, ephemeral forks).

ParamInRequiredDescription
idpathyesDesktop id (the fork source -- must be hibernated)
waitquerynoLong-poll until the operation settles (2s cadence, 120s cap)
Idempotency-KeyheadernoClient-supplied dedup key, scoped per-desktop

Request body (ForkBody):

{ "count": 3, "acknowledge_shared_state": true, "ephemeral": false }
FieldTypeRequiredDescription
countintegeryesNumber of new desktops to create from this one, 1..=20.
acknowledge_shared_statebooleanyesMust be explicitly true, or the request is rejected 400. An omitted field parses as false and is rejected -- it is never treated as an implicit true. Forks duplicate in-guest tokens/credentials from the source's memory snapshot; this is your acknowledgment of that.
ephemeralbooleannoIf true, the new desktops get a forced 900s idle_timeout_secs (auto-hibernated after 15 minutes Awake) regardless of the source's own value; otherwise they inherit it verbatim. Defaults to false.
curl -sS -X POST "$BASE/desktops/$DESKTOP_ID/fork?wait=true" \
  -H "$AUTH" -H 'Content-Type: application/json' \
  -d '{"count": 3, "acknowledge_shared_state": true}'

On success, the settled operation's result carries {"desktop_ids": ["...", "...", "..."]}. Forks come back in the Hibernated state -- wake each one on demand, they don't start Awake.

StatusMeaning
200Operation settled (result: {"desktop_ids": [...]} on success), or the ?wait=true cap was reached while still pending/running
202Operation enqueued (no ?wait=true)
400Malformed id/body, count outside 1..=20, or acknowledge_shared_state is not true
401Missing/invalid credentials
404Desktop not found or belongs to another org
409Source desktop is not hibernated
413hostd has no free capacity for the requested count (surfaced as the settled operation's own failure unless ?wait=true)
503This node is not the leader

On this page