Descanto Docs
API Reference

Computer use

Screenshot, mouse/keyboard input, and display geometry -- the look-act loop for driving an awake desktop's GUI.

The routes below only work while the desktop is awake -- a 409 means the desktop isn't awake. A 501 means this desktop's host predates the computer-use API. See Concepts: Computer use for the look → act loop these compose into, and why input is never auto-retried.

Take a screenshot

POST /v1/desktops/{id}/screenshot

ParamInRequiredDescription
idpathyesDesktop id

Request body (ScreenshotBody, all fields optional -- an empty/absent body captures the full display as binary PNG):

{ "encoding": "base64", "region": null, "scale_percent": 50 }
FieldTypeRequiredDescription
encodingstring | nullno"binary" (default): raw PNG bytes, Content-Type: image/png. "base64": a JSON body with the base64 PNG plus its post-crop/post-scale dimensions.
regionobject | nullnoOptional crop rectangle in screen pixels: {x, y, width, height}, all four required together. An out-of-screen region is clamped by the guest's own capture tool.
scale_percentinteger | nullno1-100: downscale to this percent of the (possibly cropped) size. Omitted = full size.
curl -sS -X POST "$BASE/desktops/$DESKTOP_ID/screenshot" \
  -H "$AUTH" -H 'Content-Type: application/json' \
  -d '{}' -o screenshot.png

curl -sS -X POST "$BASE/desktops/$DESKTOP_ID/screenshot" \
  -H "$AUTH" -H 'Content-Type: application/json' \
  -d '{"encoding": "base64", "scale_percent": 50}'

Response (encoding: "base64"):

{ "data_base64": "...", "width": 640, "height": 400 }
StatusMeaning
200The captured PNG -- raw bytes (default) or a base64 JSON body
400Malformed body, a partial region, or scale_percent outside 1-100
401Missing/invalid credentials
404Desktop not found or belongs to another org
409Desktop is not awake
413Screenshot larger than the 8 MiB transfer cap -- retry with region/scale_percent
500The screenshot failed inside the guest (X session down)
501This desktop's host does not support the computer-use API yet
503The desktop's host is currently unreachable -- retry
504The screenshot timed out inside the guest -- retry

Send input

POST /v1/desktops/{id}/input

Runs a batch of input actions in order, as one guest round trip regardless of batch size.

ParamInRequiredDescription
idpathyesDesktop id

Request body (InputBody):

{
  "actions": [
    { "type": "mouse_move", "x": 100, "y": 200 },
    { "type": "click", "button": "left" },
    { "type": "type", "text": "hello world" },
    { "type": "key", "combo": "ctrl+shift+t" },
    { "type": "wait", "ms": 250 }
  ]
}

actions (required, InputActionJson[]) is a type-discriminated list, capped at 50 actions, 32 KiB total text, 10 s total waits per batch:

typeFieldsNotes
mouse_movex, yAbsolute cursor move.
clickx?, y?, button?, double?Moves first if x/y given. double clicks twice, 120 ms apart.
button_pressbuttonPress-and-hold, for manual drag choreography.
button_releasebuttonRelease a held button.
dragfrom?, to, button?Drags from from (or the current cursor) to to.
scrolldirection, amount?amount 1-50, default 1.
typetext, delay_ms?1-8192 UTF-8 bytes, no NUL. delay_ms per keystroke, 0-500, default 12.
keycombo+-joined X keysyms, up to 8 tokens (e.g. "ctrl+shift+t", "Return").
waitmsIn-batch pause, 1-5000 ms.

An empty actions list is valid and performs the cursor-position read alone.

curl -sS -X POST "$BASE/desktops/$DESKTOP_ID/input" \
  -H "$AUTH" -H 'Content-Type: application/json' \
  -d '{"actions": [{"type": "click", "x": 400, "y": 300}]}'

Response (InputResponse) -- always the final cursor position after the whole batch ran:

{ "cursor": { "x": 400, "y": 300 } }

Not gated by noVNC, not transactional

API input is never gated by a noVNC control claim -- a human viewer and an API caller can interleave freely. A batch is also not transactional: a mid-batch failure (500) means every action before the failure already executed. Re-screenshot before retrying rather than blindly re-sending a batch -- a replayed click double-clicks.

StatusMeaning
200The whole batch executed; cursor is the final position
400Malformed body, unknown type/button/direction, or a batch cap exceeded
401Missing/invalid credentials
404Desktop not found or belongs to another org
409Desktop is not awake
500An action failed mid-batch -- actions before it already ran
501This desktop's host does not support the computer-use API yet
503The desktop's host is currently unreachable -- retry
504The batch timed out inside the guest

Get display geometry

GET /v1/desktops/{id}/display

One guest round trip -- there is deliberately no cached copy, so this only works while awake.

curl -sS "$BASE/desktops/$DESKTOP_ID/display" -H "$AUTH"
{ "width": 1024, "height": 768 }
StatusMeaning
200The live X display geometry
400Malformed id
401Missing/invalid credentials
404Desktop not found or belongs to another org
409Desktop is not awake
501This desktop's host does not support the computer-use API yet
503The desktop's host is currently unreachable -- retry
504The read timed out inside the guest -- retry

Set display geometry

PUT /v1/desktops/{id}/display

Request body (SetDisplayBody):

{ "width": 1280, "height": 800 }
FieldTypeRequiredDescription
widthintegeryes640-2560
heightintegeryes480-1600
curl -sS -X PUT "$BASE/desktops/$DESKTOP_ID/display" \
  -H "$AUTH" -H 'Content-Type: application/json' \
  -d '{"width": 1280, "height": 800}'

Response is the geometry actually applied, re-read from the X server after the resize -- not necessarily identical to what was requested. It survives hibernate/wake (VM-memory snapshots); a cold boot reverts to the image default 1024x768.

StatusMeaning
200The applied geometry (DisplayResponse)
400Size outside 640-2560 x 480-1600
401Missing/invalid credentials
404Desktop not found or belongs to another org
409Desktop is not awake
500The resize failed inside the guest for a non-RandR reason
501This desktop's image/X server does not support runtime RandR resizes (or its host predates the computer-use API) -- resolution is effectively fixed until the golden image gains RandR support
503The desktop's host is currently unreachable -- retry
504The resize timed out inside the guest -- retry

On this page