API keys, live vs test, and the auth header
Authentication
Authentication, at a glance
API keys, live vs test, and the auth header
X-API-Key: sk-coasty-…- 01Keylive or test identity
- 02Scopeleast privilege check
- 03Routeauthorized operation
- 04Tracerequest ID returned
Every API-key-authenticated request must include your secret key. The four health probes are public, and webhook ingress uses its documented Coasty-Signature HMAC credential instead. For API-key operations, the canonical form is the X-API-Key header, but Authorization: Bearer <key> works too: a blank X-API-Key falls through to the Bearer header. Pick one form and send the raw key. Do not paste the literal text Bearer inside X-API-Key; that is the single most common first-day mistake and it returns 401 INVALID_API_KEY. Keys are created and revoked from the API keys page. Treat a key like a password: keep it server-side, store it in an environment variable, and never commit it or ship it in client-side code.
External-machine driver routes are the exception: enrollment uses the owner API key, then returns a separate one-time device_token. That opaque token uses Authorization: Bearer, is bound to one machine, and cannot list resources, start runs, or act as the owner. See Bring your machine.
X-API-Key: sk-coasty-live-your_key_heresk-coasty-test- key never bills and runs against mock VMs, yet exercises the same core request and response shapes for sandbox-supported operations (its X-Credits-Charged and usage.cost_cents are always 0), so you can build and run CI confidently before flipping to a live key.Scopes
Authorization is deny-by-default: each endpoint enforces one named scope, and a key that lacks it gets 403 INSUFFICIENT_SCOPE with required_scope and current_scopes in the body. A newly minted key is not minimal, though. It carries all twenty ordinary scopes so you can reach every product surface without discovering a scope list first:
Exactly three scopes stay opt-in, because each is a privilege escalation rather than a product tier. Ask for them explicitly when you mint the key:
terminal:exec, files:write, snapshots:write, and machines:write are granted to a default-minted key. If you want a narrower key, mint it with an explicit scope list rather than assuming the default is minimal.Managing keys with a key
With the keys scope you can mint (POST /v1/keys), list (GET /v1/keys), and revoke (DELETE /v1/keys/{id}) keys over the API. All three are scoped to the calling key's mode. A sandbox sk-coasty-test- key sees and revokes only sandbox keys; a live key sees live keys plus legacy cua_sk_ keys, which predate the prefix split and are production credentials. A cross-mode id matches zero rows, so it returns the same 404 KEY_NOT_FOUND as an unknown id and cannot be used to probe for the existence of live keys. The list response reports key_kind (live, test, or legacy) alongside key_id, name, tier, scopes, key_prefix, created_at, and last_used_at, so you can tell a live key from a sandbox one without parsing the prefix. Both endpoints return 503 DB_UNAVAILABLE with Retry-After during a key-store outage; that is transient and never a reason to rotate a credential.
An account may hold up to 200 active keys. Keys are free, so one per service and per environment is the intended pattern rather than a shared secret; the limit exists only to bound abuse. Exceeding it returns KEY_LIMIT_REACHED.