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.