Descanto Docs
Canto

Quickstart

Create a desktop, wake it, run a command, and hibernate it.

This walks through the full desktop lifecycle once, end to end, with every step shown three ways: raw curl, the TypeScript SDK, and MCP tools. Pick whichever matches how you're integrating.

Operation handles

wake, hibernate, and destroy are asynchronous: they return an operation handle rather than blocking until the change is done. This quickstart uses ?wait=true (curl) or the SDK's default wait: true behavior to keep things linear -- see Concepts: Operations for the handle/polling model itself.

1. Get access

Canto is early access: desktops are manually provisioned, and there's no self-serve signup yet. Request an API key, then export it:

export CANTO_API_KEY="canto_sk_..."

The TypeScript SDK and the MCP server both read CANTO_API_KEY from the environment automatically. For raw curl, send it as a bearer credential on every request:

BASE=http://127.0.0.1:8081/v1
AUTH="Authorization: Bearer $CANTO_API_KEY"

(http://127.0.0.1:8081 is controld's own local-dev address and the SDK's default baseUrl -- this will change once a hosted Canto API is live; your API key request will come with the right base URL.)

2. Create a desktop

DESKTOP=$(curl -sS -X POST "$BASE/desktops" \
  -H "$AUTH" -H 'Content-Type: application/json' \
  -d '{"tier": "small", "billing_mode": "hourly"}')

DESKTOP_ID=$(echo "$DESKTOP" | jq -r .id)
echo "$DESKTOP"

tier is required (small/default/large); billing_mode defaults to hourly when omitted. A 201 means a new desktop was created; a 200 means an Idempotency-Key you sent was already used and you got the existing desktop back instead. See POST /v1/desktops.

3. Wake it

A freshly created desktop starts provisioning/hibernated, not awake -- wake it before running anything inside the guest.

curl -sS -X POST "$BASE/desktops/$DESKTOP_ID/wake?wait=true" -H "$AUTH"

?wait=true long-polls server-side (2s cadence, capped at 120s) until the operation settles, and still returns 200/202 either way -- never a timeout error. Omit it to get the operation handle back immediately (202) and poll GET /v1/operations/{id} yourself.

4. Run a command

curl -sS -X POST "$BASE/desktops/$DESKTOP_ID/exec" \
  -H "$AUTH" -H 'Content-Type: application/json' \
  -d '{"command": "echo hello", "timeout_secs": 10}'

Returns {"exit_code", "stdout", "stderr"} once the command finishes inside the guest. timeout_secs defaults to 60s and is clamped (never rejected) to 300s. See exec and files.

5. Work with files

# Write (path is given without its leading slash).
curl -sS -X PUT "$BASE/desktops/$DESKTOP_ID/files/home/user/task.txt" \
  -H "$AUTH" --data-binary "hello from canto"

# Read it back.
curl -sS "$BASE/desktops/$DESKTOP_ID/files/home/user/task.txt" -H "$AUTH"

Writes are capped at 8 MiB (413 past that). See exec and files.

6. Watch the screen

curl -sS -X POST "$BASE/desktops/$DESKTOP_ID/stream" \
  -H "$AUTH" -H 'Content-Type: application/json' \
  -d '{"claim": "view"}'

Both return a dial-able tcp://host:port/?ticket=... URL and its expiry -- a raw stream ticket, not an embeddable web player. A hosted viewer/embed integration is coming; today, point a stream-capable client at the ticket URL directly. See Streaming for the full claim/takeover model.

7. Hibernate

curl -sS -X POST "$BASE/desktops/$DESKTOP_ID/hibernate?wait=true" -H "$AUTH"

Hibernating preserves the desktop's full disk state and stops billing compute -- see Billing for exactly how awake-time is metered. Wake it again any time with the same call from step 3.

On this page