Coasty/

Developer API

Machines

Stream screenshots and execute fenced actions on your own device

Bring your machine

Bring your machine, at a glance

Stream screenshots and execute fenced actions on your own device

POST /v1/machines/external
External enrollment separates the owner API key from a machine-scoped driver credential used only for observations and commands.

External machines let the hosted Task and Workflow runtimes drive a desktop you operate. Your driver captures screenshots, uploads observations, long-polls typed commands, executes them locally, and acknowledges results. The resulting machine_id is used unchanged by POST /v1/runs, Workflow task steps, and schedules. No alternate run DSL or screenshot field is required.

1. Enroll with the owner API key. Call POST /v1/machines/external with machines:write. Protocol 1 requires screenshot; advertise only capabilities the driver really implements. Optional dimensions must be supplied as a width/height pair. Send a required, unique Idempotency-Key. The 201 response is Cache-Control: no-store and contains the machine-scoped device_token. An exact key/body replay can recover the same enrollment during its 24-hour recovery window; list and get never expose the token.

JSON
{
  "display_name": "support-laptop",
  "platform": "windows",
  "protocol_version": "1",
  "capabilities": [
    "screenshot",
    "mouse",
    "keyboard",
    "scroll"
  ],
  "screen_width": 1920,
  "screen_height": 1080,
  "metadata": {
    "environment": "production"
  }
}
Store the device token in the operating system's secret store. The owner key is for enrollment, listing, Tasks, Workflows, and revocation; the device token is accepted only for this machine's commands, observations, results, and heartbeat. Never put either credential in metadata, screenshot pixels, logs, URLs, generated code, run events, or webhooks.

Device authentication uses layered limits: before a token is trusted, the peer bucket allows 1,000 attempts/minute and 40,000/hour; after authentication, each token allows 300 requests/minute and 12,000/hour, while all device tokens on one account share 1,200 requests/minute and 48,000/hour. Screenshot observations have an additional per-token limit of 60/minute and 3,600/hour. On 429, honor the response retry metadata instead of spinning.

2. Establish liveness and submit the first frame. Send POST /v1/machines/{id}/heartbeat with Authorization: Bearer $COASTY_DEVICE_TOKEN, protocol 1, and the enrollment fencing_token. Then upload a monotonically increasing sequence to /observations. Screenshots may be raw base64 or a matching PNG/JPEG data URI, are limited to 10 MiB of base64 payload and decoded dimensions from 320×240 through 3840×2160, and must contain exactly one frame. Declared media type, optional dimensions, and optional SHA-256 must match the submitted decoded bytes.

Heartbeat returns the authoritative last_sequence (zero before any frame) and nullable last_frame_id. After a driver restart, long-poll once to recover the current fence, heartbeat with it, then submit sequence = last_sequence + 1; never guess or reset the counter to one.

Observation fieldRequiredContract
sequenceYesPositive, strictly monotonic per machine. Identical replay is safe; different bytes at the same sequence return 409.
screenshotYesBase64 PNG/JPEG, raw or matching data URI. Never a remote URL.
media_typeYesimage/png or image/jpeg; verified against decoded bytes.
sha256No64 lowercase hex characters over the submitted decoded bytes, before server normalization.
width / heightNoSupply together. If present, both must equal the decoded image dimensions.

The observation response assigns a canonical frame_id, returns verified dimensions, and echoes the current fencing generation. Its sha256 covers the server-normalized bytes and may differ from the optional request digest because metadata is stripped and the image is re-encoded. Live frames are encrypted, logically expire after 15 minutes, and are never copied into SSE, webhooks, ordinary application logs, or ordinary durable run history. This transport retention is separate from BYOK model-input auditing: if a live BYOK Task, Workflow, or schedule uses the frame for a visual decision, the fail-closed pre-provider audit retains a separate exact model-input copy under the account-lifetime screenshot rules described in Bring your own model.

3. Long-poll and execute safely. Call GET /v1/machines/{id}/commands?after=0&limit=10&wait_seconds=20. A timeout is a normal 200 with data: []. Each response carries next_cursor; persist it only after durably processing the batch, then reconnect with after=next_cursor. This produces at-least-once delivery without losing commands across restarts. The server allows exactly one queued or delivered command per external machine; concurrent dispatch returns 409 MACHINE_BUSY, preventing cross-replica races on one physical display.

Command fieldDriver rule
idStable command id. Deduplicate execution and use it in the result URL.
command / parametersTyped input action. Validate the command against local capabilities and reject unknown fields; never evaluate model raw_code.
fencing_tokenExecute only the current lease generation. An older driver must stop immediately.
precondition_frame_idThe frame on which the action was planned. If the visible screen changed, reject instead of clicking stale coordinates.
deadline_atDo not begin or acknowledge an action after its deadline.
run_idOptional correlation to the owning Task run; never use it as authorization.

Coordinates are pixels in the verified observation's exact decoded dimensions. Do not resize after capture without applying the same scale to every coordinate. Check the command deadline, fence, frame precondition, capability, and a local human-stop control immediately before input. Owner calls to the normal single-action endpoint on a connected external machine enter this same pull/result loop; they are not reported as successful until the driver acknowledges the command.

4. Acknowledge idempotently. POST { fencing_token, precondition_frame_id, success, result?, error?, observation? } to /commands/{command_id}/results. A successful result cannot carry an error; a failed result must. precondition_frame_id is required even when its value is null, and must exactly echo the command envelope. A mismatch returns 409 FRAME_PRECONDITION_MISMATCH. Embedding observation atomically supplies the post-action frame. Byte-equivalent replay returns replayed: true; a conflicting second result, stale fence, cancellation, or expiration returns 409 and must not cause local re-execution.

5. Run normal Tasks and Workflows. Once connection_status is connected, pass the external machine id as the ordinary machine_id. An offline/stale driver leaves a run waiting for observations and eventually fails with a stable machine-offline error; it never silently switches to a managed VM. Parallel Workflow task steps sharing one physical display must be serialized unless the driver explicitly provides isolated displays.

External enrollment, heartbeats, observations, command polling, and results are free and have no Coasty VM runtime charge. Managed Task/Workflow steps retain their published price. Live-key BYOK steps record provider tokens and charge zero Coasty credits. Managed-mode test-key steps remain deterministic sandbox and make no provider call; test-auth BYOK intent returns 422 LLM_PROVIDER_UNSUPPORTED before execution.GET /v1/machines/pricing reports all four transport fields as zero: enrollment, runtime per hour, direct actions, and frame transport. Those zeroes do not waive model inference performed by a Task or Workflow. Revoke the device token and cancel outstanding command leases with owner-authenticated DELETE /v1/machines/{id}. External machines have no Coasty-issued SSH, VNC, WebSocket, or browser-debug credentials, so /connection returns 409 INVALID_STATE. Because Coasty does not own the disk, /snapshot returns 400 UNSUPPORTED_MACHINE_KIND.

API reference — Coasty Computer Use API | Coasty - AI Computer-Use Agent