Run the harness on your own Anthropic or OpenAI key
Bring your own model
Bring your own model, at a glance
Run the harness on your own Anthropic or OpenAI key
X-LLM-Provider + X-LLM-Api-Key- 01Selectprovider + model
- 02Protectencrypt execution authority
- 03Callprovider processes screen
- 04Recordtokens + $0 platform
By default every LLM call in the computer-use harness runs on Coasty's managed models. With a live Coasty key, BYOK (bring your own key) flips that: opt in and the entire harness (the worker, grounding, the code agent, and compaction; every LLM call) runs on your own Anthropic or OpenAI account instead. Opt-in is always explicit, per request or per stored key. provider: "managed" (or omitting llm entirely) keeps the platform default, unchanged.
There are two ways to hand over a key. Store it once with PUT /v1/llm/keys/{provider} (encrypted with AES-256-GCM at rest and authenticated to your tenant+provider identity; only a 12-lowercase-hex sha256-prefix fingerprint is ever echoed back), or send it per request in headers. A legacy unbound stored envelope is fingerprint-checked and atomically upgraded before its plaintext can reach a provider. A header key takes precedence over the stored key. Sending a key without a provider returns 422.
POST /v1/predict, POST /v1/ground, POST /v1/sessions, and POST /v1/sessions/{id}/predict. Under test auth, predict, ground, and session create require an explicit per-request X-LLM-Api-Key plus X-LLM-Provider. A body llm.provider without that header fails closed with 422 LLM_KEY_NOT_CONFIGURED: “Stored provider keys are unavailable for test API keys. Send X-LLM-Api-Key explicitly for direct BYOK.” A test key never reads or uses a stored live provider key. Session create fixes the explicit header key for inherited session predicts but makes no inference call and bills no provider tokens; actual provider calls occur on predict, ground, and session predict. Those calls debit zero Coasty credits, but Anthropic/OpenAI can bill the supplied provider key. Task runs, both Workflow run starts, and schedules remain deterministic sandbox executions under a test Coasty key only in managed mode (llm.provider: "managed" or no BYOK intent). BYOK headers or provider intent on those async endpoints return 422 LLM_PROVIDER_UNSUPPORTED before execution. “BYOK is unavailable for synthetic test runs, workflows, and schedules. Use managed mode or a live Coasty API key.” This avoids silently ignoring a provider secret. They never decrypt or require a stored provider key and do not call or bill Anthropic/OpenAI. Test keys cannot access stored-key endpoints. Keep real provider secrets out of sandbox requests and CI fixtures. Use an sk-coasty-live- key for real asynchronous Task, Workflow, or schedule execution.X-LLM-Provider: anthropic
X-LLM-Api-Key: sk-ant-your_key_here
X-LLM-Model: claude-sonnet-5With a live Coasty key, BYOK covers seven configuration-root categories, including every LLM-backed execution path: POST /v1/predict, POST /v1/ground, POST /v1/sessions (later session predicts inherit it), POST /v1/runs, POST /v1/workflows/runs (ad-hoc), POST /v1/workflows/{workflow_id}/runs (saved), and POST /v1/schedules. The session family adds the inherited POST /v1/sessions/{id}/predict operation; the schedule family adds run-now, due, webhook, and chain fires. Parse and direct machine-action endpoints do not call an LLM. For a schedule, store the provider key first: a header key is accepted only when it matches the stored key, and each fire resolves the current stored key because request headers no longer exist then. Stored keys are managed through three endpoints, gated by the llm_keys scope (granted to new live keys by default). These endpoints require a live key; sandbox keys cannot read, overwrite, or delete the production credential store:
import os, requests
BASE = "https://coasty.ai/v1"
HEADERS = {"X-API-Key": os.environ["COASTY_API_KEY"]}
# Store (upsert) your own Anthropic key. Encrypted at rest; never echoed back.
stored = requests.put(
f"{BASE}/llm/keys/anthropic",
headers=HEADERS,
json={"api_key": os.environ["ANTHROPIC_API_KEY"]},
timeout=30,
).json()
print(stored) # {"provider": "anthropic", "key_fingerprint": "a1b2c3d4e5f6", "stored": true}
# List stored keys: provider, fingerprint, timestamps. Never the key itself.
keys = requests.get(f"{BASE}/llm/keys", headers=HEADERS, timeout=30).json()
for k in keys["keys"]:
print(k["provider"], k["key_fingerprint"])
# Delete when you rotate away (404 LLM_KEY_NOT_FOUND when none is stored)
requests.delete(f"{BASE}/llm/keys/anthropic", headers=HEADERS, timeout=30)GET /v1/llm/keys and DELETE /v1/llm/keys/{provider} return 503 DB_UNAVAILABLE with a Retry-After when the stored-key service is briefly unreachable, and the stored-key resolution path taken by every BYOK execution does the same. That path used to report a store outage as 422 LLM_KEY_INVALID, which is marked non-retryable and told a correct client its working key was bad. LLM_KEY_INVALID now means only what it says: rotate the key. DB_UNAVAILABLE means retry, and your stored keys are unchanged.Those seven start/create endpoints accept an llm object that selects the provider and, optionally, a model per harness role. It deliberately has no api_key field (a 422 if you attempt one): keys ride headers or the encrypted store only, so they can never be echoed in run objects, webhooks, or idempotency replays. The runnable examples require COASTY_API_KEY to be a live key and COASTY_MACHINE_ID to identify a real managed or developer-owned external machine; BYOK intentionally rejects sandbox authentication and mch_test_* machines.
import os, requests
BASE = "https://coasty.ai/v1"
AUTH_HEADERS = {"X-API-Key": os.environ["COASTY_API_KEY"]}
assert AUTH_HEADERS["X-API-Key"].startswith("sk-coasty-live-"), "BYOK requires a live Coasty key"
CREATE_HEADERS = dict(AUTH_HEADERS)
# Set USE_HEADER_BYOK=1 to send a request-scoped provider key; otherwise the
# previously stored Anthropic key is used. Exactly one run is created.
if os.getenv("USE_HEADER_BYOK") == "1":
CREATE_HEADERS.update({
"X-LLM-Provider": "anthropic",
"X-LLM-Api-Key": os.environ["ANTHROPIC_API_KEY"],
"X-LLM-Model": "claude-sonnet-5",
})
# Start a run on YOUR Anthropic key (stored earlier via PUT /llm/keys/anthropic).
# The llm block deliberately has NO api_key field (422 if you try): keys ride
# headers or the encrypted store only, never request bodies.
run = requests.post(
f"{BASE}/runs",
headers=CREATE_HEADERS,
json={
"machine_id": os.environ["COASTY_MACHINE_ID"],
"task": "Open the billing page and download the latest invoice as PDF",
"llm": {
"provider": "anthropic", # or "openai"; "managed" = platform default
"model": "claude-sonnet-5", # any model on your account (vision-capable)
"compaction_model": "claude-haiku-4-5", # optional per-role override
},
},
timeout=30,
).json()
# The run echoes a non-secret llm block; the key itself is never returned.
run = requests.get(f"{BASE}/runs/{run['id']}", headers=AUTH_HEADERS, timeout=30).json()
print(run["llm"]) # {"provider": ..., "model": ..., "key_fingerprint": ..., "key_source": ..., "key_scrubbed": ...}Per-role overrides exist for tuning cost against quality: run compaction on a cheaper model while the worker stays on the default, for example. With a live Coasty key, runs and workflow runs snapshot the key encrypted into their run record. This lets crash-recovery on another replica keep using your key; the ciphertext is scrubbed the moment the run reaches a terminal state. Under test auth, BYOK intent returns 422 LLM_PROVIDER_UNSUPPORTED before any snapshot or execution; only managed-mode runs use deterministic sandbox execution. GET /v1/runs/{id} echoes a non-secret llm block: {provider, model, key_fingerprint, key_source, key_scrubbed}. Workflow-run responses expose the same credential-free block. Schedules are different: live schedules store and return only the non-secret provider/model-role preference and key fingerprint, never a key, ciphertext, or key source. That preference is immutable for the schedule: PATCH rejects llm, so delete and recreate the schedule to change provider or role models. Rotate the stored key at any time; the next fire uses it without recreating the schedule. Managed-mode test-key schedules stay deterministic sandbox executions and do not resolve or call a provider; test-auth BYOK intent is rejected before schedule creation. Delete the stored key and future firings fail loudly with LLM_KEY_NOT_CONFIGURED; they never silently run on platform keys. Deletion stops future stored-key lookups, but it does not revoke the encrypted snapshot already held by an active run or workflow. Cancel each active execution to stop further provider calls; terminal transition then scrubs its ciphertext.
grounding_model to experiment before committing a cheaper or different model to that role.POST /v1/sessions resolves the BYOK provider/key once and keeps that fixed create-time configuration in the owning process's memory. Every later POST /v1/sessions/{id}/predict inherits it. Deleting or rotating the stored key does not revoke or update an active session. Use DELETE /v1/sessions/{id} (or let it expire) to stop further session calls; provider-side revocation also stops the key upstream.LLM_* codes. Provider authentication, rate-limit, quota, connection/timeout, and server failures are classified into the stable codes below. Other provider client rejections can use the endpoint's ordinary failure code, but they still never trigger platform fallback: LLM_KEY_NOT_CONFIGURED, LLM_KEY_INVALID, LLM_MODEL_INVALID, LLM_PROVIDER_AUTH_FAILED, LLM_PROVIDER_RATE_LIMITED, LLM_PROVIDER_QUOTA_EXCEEDED, and LLM_PROVIDER_ERROR.Screenshot flow: send the current PNG/JPEG as raw base64 or an exact data URI to POST /v1/predict with an instruction such as "Click Continue". Use X-LLM-Provider: anthropic plus $ANTHROPIC_API_KEY, or X-LLM-Provider: openai plus $OPENAI_API_KEY. The body is identical for both providers. The returned structured click/type/key actions use the echoed screen_width and screen_height coordinate space.
# Anthropic
curl https://coasty.ai/v1/predict -H "X-API-Key: $COASTY_API_KEY" \
-H "X-LLM-Provider: anthropic" -H "X-LLM-Api-Key: $ANTHROPIC_API_KEY" \
-H "Content-Type: application/json" \
-d '{"screenshot":"<base64-png-or-jpeg>","instruction":"Click Continue"}'
# OpenAI — same screenshot body
curl https://coasty.ai/v1/predict -H "X-API-Key: $COASTY_API_KEY" \
-H "X-LLM-Provider: openai" -H "X-LLM-Api-Key: $OPENAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"screenshot":"<base64-png-or-jpeg>","instruction":"Click Continue"}'Billing: every BYOK LLM operation debits zero Coasty platform credits. Provider-direct predict, ground, and session-predict responses report credits_charged: 0, cost_cents: 0, real provider input/output tokens, non-secret provider/model/key attribution, and platform_cost_exempt: true. Session create reports zero tokens because it configures the inherited provider key without making an inference call. Live asynchronous run and workflow creation responses cannot report future tokens; terminal run results expose usage when execution produced it. Live schedule create, get, list, PATCH, pause, and resume responses expose the non-secret llm preference. Schedule run-history records deliberately do not contain llm or future token totals. Anthropic or OpenAI bills actual tokens only when a provider-direct call occurs. Managed-mode test Task runs, Workflow task children, and schedules produce no provider token charge. BYOK intent on those test-auth async endpoints is rejected with 422 LLM_PROVIDER_UNSUPPORTED before execution. Managed requests keep the published Coasty prices; separately chosen machine, cloud, network, or third-party costs are not waived.
Data routing: when a provider-direct BYOK call occurs, Coasty transmits your prompt and screenshots to the selected Anthropic/OpenAI account. Its retention, training, region, and compliance settings are controlled by your provider agreement and account. Test-key Task, Workflow, and schedule sandbox execution sends none of that content to the configured provider. Direct BYOK predict, ground, and session-predict use a fail-closed durability boundary. Before provider egress, one transaction commits the exact caller instruction, optional custom system_prompt / instructions, selected CUA version, non-secret attribution, the input screenshot, and every direct-predict trajectory screenshot. With screenshot encryption enabled, those payloads are stored as AES-256-GCM ciphertext at rest with authenticated tenant, operation, and screenshot-slot identity; with it disabled, the exact base64 is retained for the account-lifetime audit. If the encryption preference cannot be resolved or opted-in encryption cannot be honored, provider egress is blocked. If that transaction fails, the provider is not called. After inference, the validated public response, response-visible actions/reasoning/canonical raw code, token counters, and final audit row are checkpointed before durable usage admission and idempotency completion. A same-key retry after a later settlement failure replays that checkpoint without another provider call.
That no-recall guarantee is scoped to a stable operation identity. Send an Idempotency-Key on provider-direct predict, ground, and session-predict whenever you need retry safety. An identical retry with the same key replays or safely quarantines the original operation without a second provider inference. Without an Idempotency-Key, a later HTTP request is a new operation and may call and bill your provider again. Do not automatically retry an unkeyed SETTLEMENT_INCOMPLETE; first reconcile provider usage and observed machine state.
Crash ambiguity is conservative. Once the durable journal says started, Coasty never sends that operation identity to Anthropic/OpenAI again unless a completed session owner can prove it has an in-memory replay. A crash after started but before response checkpointing returns 503 SETTLEMENT_INCOMPLETE (outcome unknown) for that operation identity. The missing response and exact token counts cannot be reconstructed, so reconcile the provider usage dashboard and observed machine state before deliberately choosing a new Idempotency-Key.
Live BYOK Tasks, Workflow task steps, and schedule firings apply the same pre-egress principle immediately before each agent.predict CUA visual decision step. Coasty synchronously commits the exact pre-predict frame (the screenshot supplied to that decision), exact effective worker instruction (including injected environment context), and non-secret attribution under an attempt-scoped request id. One immutable model_input_NNNN frame is retained per CUA visual decision step, bounded to 1,000 steps and the existing 10 MiB encoded screenshot ceiling. Synchronous delegated employees inherit the same private callback but derive deterministic tenant/root/delegation-path-bound child attempt identities and keep their own local frame sequence. Parent, sibling, nested-child, and retry rows therefore cannot skip the audit lane, overwrite one another, or collide on local step numbers. Usage accounting separately aggregates token totals and per-call role data for worker, grounding, code-agent, compaction, and derived calls; that does not mean the model-input audit copies every internal role prompt, system template, or provider payload. These rows reuse the same tenant-scoped, service-only api_requests / api_screenshots account-lifetime retention, export, and deletion rules; no separate TTL is invented. With screenshot encryption enabled, stored model-input payloads are AES-256-GCM ciphertext bound to the tenant/request/frame-slot identity; with it disabled, their exact base64 is retained. If the preference cannot be resolved or opted-in encryption cannot be honored, the provider is not called for that step. Pixels never enter Task/Workflow events, webhooks, idempotency records, or application stdout. This is not every internal provider-call prompt, a raw internal system-template transcript, or a full provider payload.