You have a task that needs a real desktop or browser. Selectors break when UI changes. API-only tools require mapping every field and edge case. The /v1/runs endpoint gives you a server-driven agent that takes screenshots, reasons, and clicks. It bills $0.05 per agent step and handles queues, retries, and human approvals.
How it works
POST /v1/runs starts a task run. You provide machine_id, task, and cua_version (default v3). You can set max_steps, deadline_seconds, and instructions (appended to the base prompt). Set on_awaiting_human to pause, fail, or cancel. Include a webhook_url to receive events. The server streams events via GET /v1/runs/{id}/events. The run transitions through queued, running, awaiting_human, succeeded, failed, cancelled, or timed_out.
curl -X POST https://coasty.ai/v1/runs \
-H 'X-API-Key: $COASTY_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
"machine_id": "vm-12345",
"task": "Open Chrome and navigate to https://example.com",
"cua_version": "v3",
"max_steps": 50,
"deadline_seconds": 600,
"on_awaiting_human": "pause",
"webhook_url": "https://your-server.com/coasty-events"
}'Running and monitoring the task
- GET /v1/runs lists all runs for your account.
- GET /v1/runs/{id} returns the final status and metadata.
- GET /v1/runs/{id}/events streams events via Server-Sent Events. Use the Last-Event-ID header to reconnect.
- States include queued, running, awaiting_human, succeeded, failed, cancelled, and timed_out.
import os
import requests
from urllib.parse import urljoin
from requests.exceptions import RequestException
COASTY_API_KEY = os.getenv('COASTY_API_KEY')
BASE_URL = 'https://coasty.ai/v1'
def create_run(machine_id: str, task: str, cua_version: str = 'v3',
max_steps: int = 50, deadline_seconds: int = 600,
on_awaiting_human: str = 'pause', webhook_url: str = None) -> dict:
url = urljoin(BASE_URL, 'runs')
payload = {
'machine_id': machine_id,
'task': task,
'cua_version': cua_version,
'max_steps': max_steps,
'deadline_seconds': deadline_seconds,
'on_awaiting_human': on_awaiting_human
}
if webhook_url:
payload['webhook_url'] = webhook_url
resp = requests.post(url, headers={'X-API-Key': COASTY_API_KEY, 'Content-Type': 'application/json'}, json=payload)
resp.raise_for_status()
return resp.json()
def get_run(run_id: str) -> dict:
url = urljoin(BASE_URL, f'runs/{run_id}')
resp = requests.get(url, headers={'X-API-Key': COASTY_API_KEY})
resp.raise_for_status()
return resp.json()
def stream_events(run_id: str):
url = urljoin(BASE_URL, f'runs/{run_id}/events')
resp = requests.get(url, headers={'X-API-Key': COASTY_API_KEY}, stream=True)
resp.raise_for_status()
for line in resp.iter_lines():
if line:
print(line.decode('utf-8'))
if __name__ == '__main__':
run = create_run(machine_id='vm-12345', task='Open Chrome and navigate to https://example.com')
print('Run ID', run['id'])
stream_events(run['id'])
# After streaming, call get_run to fetch final status and metadata.POST /v1/runs starts a server-driven computer use agent that bills $0.05 per step and streams events for real-time monitoring.
Where this beats brittle automation
API-only tools need stable selectors and field IDs. When a UI changes, your automation breaks. The computer use agent sees the screen, understands the context, and clicks or types naturally. It handles dynamic layouts, popups, and browser state. You focus on the task prompt, not the DOM structure.
Start building autonomous agents with the /v1/runs endpoint. Deploy a machine, define a task, and watch the agent finish it. Get a key at https://coasty.ai/developers and see the code in action.
Want to see this in action?
View Case Studies