Coasty/

Developer API

Tasks

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
A task run owns the screenshot loop, execution, event history, budget, and completion check for one goal on one machine.

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.

A live Coasty key executes a BYOK Task on the selected provider. Under test auth, a managed-mode Task is deterministic and sandboxed. Any BYOK header or provider metadata returns 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": ...}
FieldRequiredDescription
machine_idYesA connected managed or external machine the agent will drive.
taskYesThe natural-language goal to accomplish.
cua_versionNoModel family. v5 by default; v1 / v3 / v4 / v5 on all tiers.
instructionsNoExtra guidance appended to the base prompt.
system_promptNoA preamble placed ahead of the base prompt.
modelNoLegacy top-level selector; prefer llm.model for BYOK. Uses the 1-256-character safe model-id grammar. Secret-shaped values are rejected without reflection as 422 LLM_MODEL_INVALID; credentials belong only in X-LLM-Api-Key.
max_stepsNoHard cap on agent steps (default 150, ceiling 1,000).
action_policyNoImmutable post-model controls inherited by the complete task execution tree; max_actions is cumulative across nested delegation.
deadline_secondsNoWall-clock budget; the run becomes timed_out if breached.
on_awaiting_humanNoWhat to do when a human is needed: pause (default), fail, or cancel.
awaiting_human_timeout_secondsNoHow long to wait for a human before timing out.
webhook_urlNoHTTPS endpoint for lifecycle callbacks (https only).
metadataNoArbitrary JSON echoed back on the run object.
A Task Run's 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.
EndpointPurpose
POST /v1/runsStart a run. Create and exact bounded replay responses include webhook_secret; GET/list do not.
GET /v1/runsList runs. Filter with ?status= and ?limit=.
GET /v1/runs/{id}Fetch a single run and its current status.
GET /v1/runs/{id}/eventsServer-Sent Events stream of the run (see Streaming events).
GET /v1/runs/{id}/logThe assembled per-step log: what the run actually did, as plain JSON. The supported way to read a finished run's full trajectory (see Reading a finished run).
POST /v1/runs/{id}/cancelCancel a run that has not reached a terminal state.
POST /v1/runs/{id}/resumeHand control back after a human takeover.
JSON
{
  "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"
}
FieldTypeDescription
idstringUnique run id (UUID).
objectstringAlways "agent.run".
statusstringqueued, running, awaiting_human, succeeded, failed, cancelled, or timed_out.
machine_idstring|nullThe machine the agent is driving; null while a submit-and-forget task is provisioning.
machineobject|nullAutomatic provisioning and cleanup lifecycle for POST /v1/tasks; null on caller-supplied-machine runs. Its status is one of provisioning, starting, reconciling, ready, terminated, or cleanup_failed, alongside cleanup_status, machine_id, ttl_minutes, and an error object when one was recorded.
taskstringThe natural-language goal you submitted.
cua_versionstringModel family: "v5" (default). Any of "v1" / "v3" / "v4" / "v5", available on all tiers.
instructionsstringExtra guidance appended to the base prompt (nullable).
max_stepsintHard cap on agent steps (default 150).
deadline_secondsintEffective wall-clock budget after the server applied its ceiling. Echoed back so a clamp is never silent: request a value above the ceiling and this shows what the run is actually held to.
awaiting_human_timeout_secondsintEffective ceiling-applied budget for how long the run may sit in awaiting_human before timing out.
on_awaiting_humanstringWhat to do when a human is needed: pause, fail, or cancel.
steps_completedintHow many agent steps have run so far.
credits_chargedintDeveloper API wallet units billed (1 unit = $0.01).
cost_centsintUSD-cent amount, numerically equal to credits_charged; divide by 100 for USD.
resultobject{ passed, status, summary?, usage, verdict? } once the run finishes. verdict is present only when a completion verifier ran. summary is the LAST 2,000 characters of the trajectory, so the agent's final answer survives; it is a tail excerpt, not the whole trajectory. summary is ABSENT on a plain failed run (that branch returns only { passed: false, status: "fail" }), which is exactly when you most want it, so do not index into it unguarded. There is no output field: read the full trajectory with GET /v1/runs/{id}/log, which works on every terminal status.
errorobject{ code, message } when the run failed (nullable).
awaiting_human_reasonstringWhy the run paused for a human (nullable).
metadataobjectThe metadata you attached at create time.
llmobject|nullNon-secret BYOK echo when the run opted into your own key: { provider, model, key_fingerprint, key_source, key_scrubbed }. Never contains the key itself. See Bring your own model.
webhook_urlstringWhere lifecycle events are POSTed (nullable).
created_atstringISO-8601 creation timestamp.
started_atstringWhen the run left the queue (nullable).
awaiting_human_sincestringWhen the run last paused for a human (nullable).
finished_atstringWhen the run reached a terminal state (nullable).
request_idstringId of the create request, for support and tracing.
A run moves through 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.
API reference — Coasty Computer Use API | Coasty - AI Computer-Use Agent