Give the agent a task and a machine; it drives to done
Task runs
Task runs, at a glance
Give the agent a task and a machine; it drives to done
POST /v1/runs- 01Taskgoal + machine
- 02Loopobserve, predict, act
- 03Guardbudget + policy + deadline
- 04Resultdurable terminal object
A run hands the agent a task and a machine, then drives it to completion on our side. The agent loops autonomously, verifies its own work (pass or fail), can pause for a human when it hits a wall, bills managed inference at $0.05 per completed step from your dollar API wallet ($0.08/step on v1) while BYOK bills $0 to Coasty, and streams successfully persisted timeline events with ordered at-least-once replay. Intermediate events can be absent, so the run resource is authoritative. You start one call and watch, instead of running the predict loop yourself.
422 LLM_PROVIDER_UNSUPPORTED before execution; no stored provider key is read or decrypted, and Anthropic/OpenAI is not called or billed.The machine may be kind: managed or kind: external. For an external target, start its driver first and wait for connection_status: connected; the run obtains screenshots and delivers typed actions through the enrolled machine protocol. It never falls back to a hosted VM or asks you to place screenshot bytes in the run request.
Create a run with POST /v1/runs. The two required fields are machine_id and task. The response is an agent.run object with status of queued, plus a create-response-only webhook_secret you store to verify webhooks. Send an Idempotency-Key header to make a retried create safe. That key deduplicates the top-level run resource, not every later OS side effect: use idempotent, checkpointed tasks and verify state before irreversible actions.
import os, time, requests
BASE = "https://coasty.ai/v1"
HEADERS = {"X-API-Key": os.environ["COASTY_API_KEY"]}
TERMINAL = {"succeeded", "failed", "cancelled", "timed_out"}
# 1. Start a run. Idempotency-Key makes a retried create safe.
run = requests.post(
f"{BASE}/runs",
headers={**HEADERS, "Idempotency-Key": "order-4821"},
json={
"machine_id": "mch_test_0123456789abcdef",
"task": "Open the billing page and download the latest invoice as PDF",
"cua_version": "v5", # any of v1/v3/v4/v5, all tiers; omit to use the v5 default
"max_steps": 40,
"on_awaiting_human": "pause",
},
timeout=30,
).json()
run_id = run["id"]
print(run["status"]) # "queued"
webhook_secret = run.get("webhook_secret") # create/replay response only; store it now
# 2. Poll until terminal.
while True:
run = requests.get(f"{BASE}/runs/{run_id}", headers=HEADERS, timeout=30).json()
print(run["status"], run["steps_completed"], "steps")
if run["status"] in TERMINAL:
break
time.sleep(2)
print(run["result"]) # {"passed": ..., "status": ..., "summary": ...}action_policy is validated at create time, atomically persisted with the run, restored after worker recovery, and inherited by nested CUA delegation. Every proposed batch is checked before dispatch and max_actions is cumulative across the complete execution tree.{
"id": "7a1b2c3d-4e5f-4678-9abc-def012345678",
"object": "agent.run",
"status": "queued",
"machine_id": "mch_test_0123456789abcdef",
"task": "Open the billing page and download the latest invoice as PDF",
"cua_version": "v5",
"instructions": null,
"max_steps": 40,
"deadline_seconds": 1800,
"awaiting_human_timeout_seconds": 1800,
"on_awaiting_human": "pause",
"steps_completed": 0,
"credits_charged": 0,
"cost_cents": 0,
"result": null,
"error": null,
"awaiting_human_reason": null,
"metadata": {
"team": "finance"
},
"webhook_url": "https://example.com/hooks/coasty",
"created_at": "2026-06-01T12:00:00Z",
"started_at": null,
"awaiting_human_since": null,
"finished_at": null,
"request_id": "req_4f9a2b1c",
"webhook_secret": "whsec_one_time_value_shown_here"
}queued to running, can bounce between running and awaiting_human, and ends in one of succeeded, failed, cancelled, or timed_out. Terminal states are immutable, so it is always safe to stop polling once you reach one. Runs need the runs:read and runs:write scopes, granted to new keys by default.