---
title: "Long-poll commands for an external machine"
method: GET
path: "/v1/machines/{machine_id}/commands"
tags: ["machines"]
---

# Long-poll commands for an external machine

`GET /v1/machines/{machine_id}/commands`

DEVICE TOKEN ONLY. Returns commands after an exclusive cursor. Persist next_cursor only after durably processing the batch. A long-poll timeout returns 200 with data=[]; reconnect with the last committed cursor. The server permits exactly one queued or delivered command per external machine; concurrent dispatch returns 409 MACHINE_BUSY, preventing cross-replica races on one physical display. Each command is fenced, deadline-bound, and tied to the frame in precondition_frame_id. A driver must reject expired, stale-fenced, unsupported, or visibly stale commands instead of executing them.

## Query parameters

- `after` integer
- `limit` integer
- `wait_seconds` number

## Response `200`

Command batch, or an empty data array when the long poll expires.

- ExternalMachineCommandsResponse
  - `machine_id` string, required
  - `data` ExternalMachineCommandEnvelope[], required
    - `id` string, uuid, required
    - `cursor` integer, required
    - `protocol_version` '1', required
    - `command` 'screenshot' | 'detect_elements' | 'ocr' | 'click' | 'double_click' | 'click_with_modifiers' | 'drag' | 'move' | 'list_windows' | 'switch_to_window' | 'close_window' | 'minimize_window' | 'maximize_window' | 'restore_window' | 'arrange_windows' | 'move_window' | 'type' | 'key_press' | 'key_combo' | 'scroll' | 'terminal_connect' | 'terminal_execute' | 'terminal_read' | 'terminal_type' | 'terminal_clear' | 'terminal_close' | 'execute_command' | 'execute_terminal_command' | 'file_read' | 'file_exists' | 'directory_list' | 'file_write' | 'file_edit' | 'file_append' | 'file_delete' | 'directory_delete' | 'file_download' | 'file_list_downloads' | 'file_upload' | 'browser_open' | 'browser_connect' | 'browser_navigate' | 'browser_click' | 'browser_type' | 'browser_get_dom' | 'browser_get_clickables' | 'browser_state' | 'browser_info' | 'browser_get_context' | 'browser_scroll' | 'browser_close' | 'browser_screenshot' | 'browser_wait' | 'browser_list_tabs' | 'browser_open_tab' | 'browser_close_tab' | 'browser_switch_tab' | 'browser_execute', required — Canonical, allowlisted device-plane command. Owner actions and internal Task/Workflow CUA execution share this transport. Drivers must implement only the capabilities they advertise; the bundled reference driver intentionally implements the narrower screenshot/mouse/keyboard/scroll subset. raw_code, done, and fail are never sent to a device.
    - `parameters` object, required — Command-specific parameters already validated against the canonical ActionRequest schema before dispatch. Drivers must still validate the shape for command and their advertised capabilities before local execution.
    - `run_id` string, nullable
    - `deadline_at` string, date-time, required
    - `fencing_token` integer, required — Lease generation. Results with an older or mismatched generation are rejected.
    - `precondition_frame_id` string, uuid, nullable, required — The exact frame on which the action was planned. Do not execute after the visible screen has changed.
  - `next_cursor` integer, required — Resume with after=next_cursor after durably processing this batch.
  - `fencing_token` integer, required
  - `request_id` string, required

## Other responses

- `400` — Invalid request body or parameters.
- `401` — Missing, malformed, revoked, or machine-mismatched external device token. Pass the one-time machine token as `Authorization: Bearer <device_token>`; owner API keys are not accepted on device-plane routes. Revoked tokens may return DEVICE_REVOKED.
- `403` — API key lacks the required scope or tier-feature is unavailable on the caller's plan.
- `404` — Resource not found in this key's namespace.
- `409` — The resource state conflicts with this operation.
- `413` — The request body exceeds the endpoint limit.
- `422` — The JSON shape is valid but one or more values violate the endpoint contract.
- `429` — Rate or concurrency limit exceeded.
- `500` — Unexpected server error. Retry with exponential backoff.
- `502` — An upstream dependency returned an invalid response.
- `503` — A required service is temporarily unavailable.
- `504` — An upstream dependency timed out.

---

[API](https://skmtc.net/coasty/apis/coasty-public-api.md) · [All operations](https://skmtc.net/coasty/apis/coasty-public-api/llms.txt) · [OpenAPI document](https://skmtc-service-staging.skmtc.workers.dev/v1/apis/coasty/coasty-public-api/revisions/f50d93b0d8a1/schema)
