Coasty/

Developer API

Machines

SSH/VNC, screenshots, snapshots, and the action surface

Connect & control

Connect & control, at a glance

SSH/VNC, screenshots, snapshots, and the action surface

POST /v1/machines/{id}/actions
Connection endpoints expose screenshots, actions, browser, terminal, and files through the same tenant-owned machine boundary.

SSH/VNC credentials apply only to Coasty-managed machines; the direct-control and agent-run surfaces support both managed and caller-owned external machines. Once a machine is running, you can drive it through Coasty's control endpoints or hand it to an agent run; managed machines can also be reached directly over SSH/VNC. Normal machine responses redact secrets and only expose has_ssh_key / has_vnc_password booleans plus ports.

Connection secrets. GET /v1/machines/{id}/connection returns the full ssh_private_key_pem, vnc_password, public IP, and ports. It is gated by the opt-in connection:read scope (not granted by default — request it when you mint the key), and the response is sent Cache-Control: no-store. Treat that payload like a password: never log it. SSH usernames are ubuntu on Linux and Administrator on Windows; VNC details exist only on desktop_enabled machines.

curl — fetch SSH key + VNC password (needs connection:read)
curl https://coasty.ai/v1/machines/$MACHINE_ID/connection \
  -H "X-API-Key: $COASTY_API_KEY"

Screenshots & snapshots. GET /v1/machines/{id}/screenshot returns the current screen of a desktop machine (a still-booting VM returns 502 SCREENSHOT_FAILED — poll for running first). An external response also returns a canonical frame_id for those exact pixels; a fresh capture is a safe read-only resynchronization and advances that frame. POST /v1/machines/{id}/snapshot captures a restorable image (Linux), needs the snapshots:write scope, and is the one machine op with a flat fee ($0.01 per snapshot). A conclusive pre-creation failure is refunded, confirmed by X-Credits-Refunded. A timeout, an upstream 5xx, or malformed post-dispatch result may already have created the image and instead returns terminal 503 SNAPSHOT_OUTCOME_UNKNOWN without a blind refund or re-execution. Boot a future machine from a confirmed snapshot with restore_from_snapshot: true.

The control surface. Drive the VM directly with these endpoints. Each enforces a specific scope, and the high-risk ones — browser_execute (arbitrary JS) and anything under connection:read — are opt-in. /actions/batch runs up to 50 steps and stops on the first error by default (stop_on_error: false to continue); /terminal truncates output to 5000 chars.

Strict command contracts. /actions is a 50-command discriminated API, not a free-form command tunnel. The selected command chooses an exact parameter schema; unknown keys, incomplete alias pairs, mixed mutually exclusive shapes, non-JSON values, and parameter objects over 1,000,000 bytes are rejected before dispatch. Discover the exact command list from GET /v1/models under machine_action_commands, and generate validators from the OpenAPI ActionRequest.oneOf catalog.

External GUI control is an observation-bound loop: capture a screenshot, plan only against those pixels (directly or through POST /v1/predict), and send one action with that frame_id as precondition_frame_id. The driver rechecks the screen immediately before OS input and atomically returns the post-action screenshot, new frame_id, and observation_available. If pixels are unavailable after a committed mutation, capture again and re-plan; never repeat the mutation just to recover an image. Prefer single actions over external GUI batches because a future step cannot safely name a frame that does not exist yet. Missing mutation frames return 409 FRAME_PRECONDITION_REQUIRED; stale frames return 409 FRAME_PRECONDITION_MISMATCH or a typed unsuccessful device result.
MethodPathScopeSummary
POST/v1/machinesmachines:writeProvision a new VM. Idempotent with Idempotency-Key.
POST/v1/machines/externalmachines:writeEnroll your own machine; requires Idempotency-Key and returns its recoverable enrollment token with no-store.
GET/v1/machinesmachines:readList your machines (newest first). Supports ?limit=1–200.
GET/v1/machines/{id}machines:readFetch one machine, with redacted connection metadata.
PATCH/v1/machines/{id}machines:writeSet or clear resource expiry: managed destroy or external registration revoke (ttl_minutes).
DELETE/v1/machines/{id}machines:writeTerminate a managed VM or revoke an external machine and its outstanding command leases.
POST/v1/machines/{id}/startmachines:writeBoot a stopped/error machine. Async — poll for running.
POST/v1/machines/{id}/stopmachines:writePower off a running machine (disk preserved). Async.
POST/v1/machines/{id}/restartmachines:writeReboot the machine. Async.
POST/v1/machines/{id}/snapshotsnapshots:writeManaged only; external machines return 400 UNSUPPORTED_MACHINE_KIND.
GET/v1/machines/{id}/screenshotmachines:readCurrent screen as a JPEG/PNG (desktop machines).
GET/v1/machines/{id}/connectionconnection:readManaged only; external machines return 409 INVALID_STATE and receive no Coasty credentials.
GET/v1/machines/pricingmachines:readThe live runtime rate card.
POST/v1/machines/{id}/actionsper-commandRun one control action (click/type/etc.) on the VM.
POST/v1/machines/{id}/actions/batchper-commandRun up to 50 actions in sequence. Union of step scopes.
POST/v1/machines/{id}/browser/{op}actions:exec*Browser ops (navigate, click, …). browser_execute needs browser:execute.
POST/v1/machines/{id}/terminalterminal:execRun a shell command. Output truncated to 5000 chars.
POST/v1/machines/{id}/files/{op}files:read/writeFile ops. Reads need files:read; writes need files:write.
GET/v1/machines/{id}/commandsdevice tokenLong-poll fenced typed commands after a durable cursor.
POST/v1/machines/{id}/observationsdevice tokenUpload a monotonic PNG/JPEG screenshot observation.
POST/v1/machines/{id}/commands/{command_id}/resultsdevice tokenAcknowledge one command; may include the post-action frame.
POST/v1/machines/{id}/heartbeatdevice tokenRenew driver liveness with the current fencing token.
curl — run a shell command on the VM
curl -X POST https://coasty.ai/v1/machines/$MACHINE_ID/terminal \
  -H "X-API-Key: $COASTY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"command": "ls -la /home/ubuntu"}'

Idempotency & safety. The machine operations in the exact reserve-and-replay set — provision, snapshot, actions, action batches, browser, terminal, and file operations — accept an Idempotency-Key (≤128 chars). Lifecycle start/stop/restart, TTL updates, and termination do not. For supported operations, a duplicate key replays the original result without re-executing; a duplicate that arrives while the first is still running waits up to ~25s, then returns 409 IDEMPOTENCY_IN_FLIGHT. One nuance worth knowing: a command that never reached the VM (a dispatch failure) is not cached, so retrying the same key runs it fresh; a command the VM actually ran (even if it errored) is cached. Screenshot pixels are response-only and are never kept in the generic replay cache. A replay does not execute again: it retains frame_id, but returns screenshot: null and observation_available: false. Every machine lookup is ownership-scoped, so a wrong or someone-else's id returns 404 — never a leak. Full code list in Errors.

Idempotency keys are global within one concrete API-key credential and live/test mode, and the fingerprint includes the operation plus canonical JSON. Never reuse a key on another endpoint; even an identical body returns 422 IDEMPOTENCY_KEY_REUSED. Rotating the owner API key starts a new replay namespace. External-machine enrollment is intentionally account-scoped so an exact enrollment replay can recover its device token after key rotation. BYOK-capable operations also bind the non-secret effective provider, model roles, and one-way key fingerprint; plaintext provider keys never enter the replay hash.
API reference — Coasty Computer Use API | Coasty - AI Computer-Use Agent