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- 01Enrollowner key + capabilities
- 02Tokendriver-only credential
- 03Exchangescreenshots and commands
- 04Reportfenced results
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.
{
"display_name": "support-laptop",
"platform": "windows",
"protocol_version": "1",
"capabilities": [
"screenshot",
"mouse",
"keyboard",
"scroll"
],
"screen_width": 1920,
"screen_height": 1080,
"metadata": {
"environment": "production"
}
}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.
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.
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.