| 401 | INVALID_API_KEY | Key missing, malformed, or revoked (or "Bearer " was wrongly pasted into X-API-Key). 401s carry a WWW-Authenticate header. |
| 403 | INSUFFICIENT_SCOPE | The key is valid but lacks the scope this endpoint needs. The body lists required_scope and current_scopes; re-mint a key with the scope. |
| 402 | INSUFFICIENT_CREDITS | Your USD wallet can't cover the request. The body reports required and balance. Add funds, or use a test key while building. |
| 402 | WALLET_EXHAUSTED | The wallet emptied mid-run. Steps that already completed were billed; top up to continue. |
| 422 | VALIDATION_ERROR | The body failed schema validation. error.details lists the offending field path and the expected type. |
| 422 | ACTION_POLICY_VIOLATION | A server-enforced action policy rejected the complete action batch before dispatch. Direct machine actions and Parse return this error envelope; Predict and Sessions return status=fail with a fail action carrying the same code. |
| 422 | INVALID_SCREENSHOT | The screenshot is not a static PNG/JPEG encoded as raw base64 or an exact image/png or image/jpeg data URI, or it contains whitespace. |
| 413 | PAYLOAD_TOO_LARGE | An individual screenshot exceeds its 10,485,760-character base64 limit, or the complete JSON request exceeds 15 MiB. Downscale/JPEG-compress frames or use a stateful session. |
| 400 | INVALID_LIMIT | A ?limit= query parameter fell outside the allowed range of 1 to 200. The body echoes actual, min, and max. |
| 400 | INVALID_EVENT_CURSOR | A cursor query parameter is negative. GET /v1/runs/{id}/log requires after_step to be a non-negative integer. |
| 400 | INVALID_STATUS_FILTER | A ?status= query parameter is not one of the real statuses for that resource. |
| 404 | NOT_FOUND | The resource id is unknown or expired. Ids are mode-isolated, so a test key can't see live resources. |
| 404 | SESSION_NOT_FOUND | The session id is unknown or its 2-hour (7200-second) idle window expired. |
| 404 | RUN_NOT_FOUND | The run id is unknown, expired, or belongs to the other key mode. |
| 404 | WORKFLOW_NOT_FOUND | The workflow id (or workflow-run id) is unknown or was archived. |
| 409 | NOT_AWAITING_HUMAN | You resumed a run that is not in awaiting_human. The body reports current_state and required_state. |
| 409 | RESUME_CONFLICT | A resume or cancel race was lost (the run already moved on). Re-read the run and retry against its new state. |
| 422 | IDEMPOTENCY_KEY_REUSED | The same Idempotency-Key was sent with a different operation, canonical body, or effective BYOK provider/model/key identity. Use a fresh key, or replay every original bound input. |
| 409 | IDEMPOTENCY_IN_FLIGHT | The original keyed request is still running. Honor Retry-After and retry the identical body with the same key. |
| 409 | IDEMPOTENCY_ALREADY_REFUNDED | This key's stable wallet debit was already refunded and cannot fund new work. Use a new Idempotency-Key for a new execution. |
| 400 | FEATURE_NOT_AVAILABLE | A requested option is not shipped on this API surface or is unavailable on your tier. Remove the option, use a supported surface, or upgrade when the feature is tier-gated. |
| 500 | INTERNAL_ERROR | An unexpected server error. Retry, and quote request_id when contacting support. |
| 500 | PREDICTION_FAILED | The prediction model run failed. X-Credits-Refunded confirms that its wallet debit was returned. |
| 500 | GROUNDING_FAILED | The grounding model run failed. X-Credits-Refunded confirms that its wallet debit was returned. |
| 503 | BILLING_UNAVAILABLE | The wallet charge or refund could not be confirmed. Follow retry_with_same_idempotency_key exactly; do not infer settlement from the status alone. |
| 503 | SETTLEMENT_INCOMPLETE | A provider/session result crossed an irreversible boundary but final persistence is incomplete. With an Idempotency-Key, retry the identical body using that same key: a durable completion replays without provider work and a started-only outcome is never re-inferred. Without a key, another request is a new operation that may call and bill the provider again; do not auto-retry. |
| 503 | UPSTREAM_UNAVAILABLE | A transient upstream outage. Retry with backoff. Reuse an Idempotency-Key only when the original operation is marked reserve-and-replay and the response explicitly permits same-key retry. |
| 504 | UPSTREAM_TIMEOUT | An upstream call timed out. Check the original operation's idempotency policy before retrying; unsupported mutations have no duplicate-suppression guarantee. |
| 404 | MACHINE_NOT_FOUND | The machine id is unknown, was terminated, or belongs to the other key mode. Machine ids are mode-isolated, so a test key can't see a live VM. Returned (not 403) even for someone else's id, so ids can't be enumerated. |
| 400 | INVALID_MACHINE_ID | The path id is not a UUID (live) or an mch_test_<hex> id (test). Use the id returned by POST /v1/machines verbatim. |
| 409 | INVALID_STATE | A lifecycle action conflicts with the machine's status (e.g. start while running). The body carries current_state and allowed_from. Re-read the machine and act on its real state. |
| 400 | INVALID_TTL | ttl_minutes must be 0 (clear auto-destroy) or 5–10080 (5 min to 7 days). A value of 1–4 or above 10080 is rejected. |
| 409 | TEST_MACHINE_LIMIT | A test key may hold at most 5 mock machines at once. Delete one, or move to a live key for real VMs. |
| 400 | UNKNOWN_BROWSER_OP | The /browser/{op} path segment is not a known browser operation. See the action surface table for valid ops. |
| 400 | UNKNOWN_FILE_OP | The /files/{op} path segment is not a known file operation. See the action surface table for valid ops. |
| 502 | SCREENSHOT_FAILED | The VM could not produce a screenshot (still booting, or the desktop service is not ready). Poll until status is running, then retry. |
| 502 | UPSTREAM_AUTH_FAILED | The provisioning service rejected our internal call (a Coasty-side configuration issue, never your key). Transient from your side; honor Retry-After and retry, and quote request_id if it persists. |
| 401 | INVALID_DEVICE_TOKEN | The external-machine token is missing, malformed, revoked, or belongs to another machine. Re-enroll or use that machine's one-time device token; never substitute the owner API key. |
| 401 | DEVICE_REVOKED | The external machine was revoked by its owner. Stop the driver permanently; an old token cannot revive the machine. |
| 409 | STALE_FENCING_TOKEN | The driver is using an older lease generation. Stop executing commands and re-authenticate; a stale driver must never write observations or results. |
| 409 | FRAME_SEQUENCE_CONFLICT | An observation sequence was reused with different screenshot bytes. Resume after the last accepted monotonic sequence instead of overwriting it. |
| 409 | FRAME_PRECONDITION_REQUIRED | A mutating external-machine action omitted the latest observed frame_id. Capture, plan one action, then submit it as precondition_frame_id. |
| 409 | FRAME_PRECONDITION_MISMATCH | The supplied frame is stale, or the terminal device result did not exactly echo its envelope's precondition_frame_id (including null). Capture again and re-plan; never re-execute the input blindly. |
| 409 | COMMAND_RESULT_CONFLICT | A completed command id received a different terminal result. Identical replay is safe; conflicting replay is rejected. |
| 409 | COMMAND_EXPIRED | The command deadline passed before its result arrived. Do not execute or acknowledge the expired action. |
| 409 | COMMAND_CANCELLED | The run or owner cancelled the command before completion. Stop local execution and do not replay it. |
| 409 | MACHINE_BUSY | The external machine already has one queued or delivered command. Wait for that stable command id to finish; do not dispatch concurrently to the same physical display. |
| 409 | PROTOCOL_VERSION_MISMATCH | The heartbeat protocol_version differs from the enrolled driver's protocol. Upgrade or re-enroll instead of guessing at wire compatibility. |
| 503 | DB_UNAVAILABLE | A backing store was briefly unreachable: the machine registry, the run event log behind GET /v1/runs/{id}/log, the schedule store behind the schedule reads, the Coasty API key store behind GET/DELETE /v1/keys, or the BYOK stored-key service behind GET/DELETE /v1/llm/keys and stored-key resolution. Transient; honour Retry-After and retry with backoff. Your stored data is unchanged, so never rotate a credential in response to this. |
| 503 | SNAPSHOT_OUTCOME_UNKNOWN | Snapshot dispatch may have created the machine image, but completion could not be confirmed. The keyed result is terminal and the debit is retained for reconciliation; do not retry blindly. |
| 422 | LLM_KEY_NOT_CONFIGURED | You asked for BYOK (llm.provider is anthropic or openai) but no usable key was supplied. Live auth can use the encrypted store or X-LLM-Api-Key. Test auth never reads stored live keys and returns: Stored provider keys are unavailable for test API keys. Send X-LLM-Api-Key explicitly for direct BYOK. |
| 422 | LLM_KEY_INVALID | The supplied LLM key is unusable: empty, malformed, the wrong provider's format, or a stored key that could not be decrypted. Check the key matches the provider (sk-ant-... for anthropic) and re-store it. |
| 422 | LLM_MODEL_INVALID | A BYOK or legacy top-level Run model id is empty, longer than 256 characters, uses unsupported characters, or looks like a credential (sk-*, bearer/authorization, API-key, or secret form). Rejection never reflects the supplied value. Put credentials only in X-LLM-Api-Key. |
| 404 | LLM_KEY_NOT_FOUND | DELETE /v1/llm/keys/{provider} found no stored key for that provider. List what you have with GET /v1/llm/keys. |
| 422 | LLM_PROVIDER_UNSUPPORTED | The provider/header combination is unsupported, or test-auth async work carried BYOK intent. Synthetic test Runs, Workflows, and Schedules return: BYOK is unavailable for synthetic test runs, workflows, and schedules. Use managed mode or a live Coasty API key. |
| 401 | LLM_PROVIDER_AUTH_FAILED | Your Anthropic/OpenAI account rejected the key (their 401/403). Rotate the key in your provider console and update it with PUT /v1/llm/keys/{provider}. Never retried on platform keys. |
| 429 | LLM_PROVIDER_RATE_LIMITED | Your own provider account is rate-limiting (their 429). Retryable: honor Retry-After, raise your provider tier, or reduce volume. |
| 402 | LLM_PROVIDER_QUOTA_EXCEEDED | Your provider account is out of credits or quota. Top up your Anthropic/OpenAI billing; this is your provider's balance, not your Coasty wallet. |
| 502 | LLM_PROVIDER_ERROR | Your LLM provider returned a server error (their 5xx). Retryable with backoff; if it persists, check the provider's status page. |