{"openapi":"3.1.0","info":{"title":"Drums Cloud gateway (drumsd-compatible \/v1)","version":"0.4.2","description":"The drumsd \/v1 API as served to one workspace through the Drums Cloud gateway. Authenticate with a Drums API key. Slot ids are computer UUIDs and saved-state ids are snapshot UUIDs; base images keep their names. Records omit host internals (url, port, extra_ports, init_pid, init_start_ticks, num). Quota and budget refusals are 403 (never 429); 429 means no capacity right now."},"paths":{"\/v1\/checkpoints":{"get":{"summary":"List checkpoints","description":"Every adopted or captured checkpoint with its template server. Each checkpoint carries `provenance` (the digests of the runtime sources and of the image inspection it was made with) and `stale`\/`stale_reason`: an installed image made with other ones than the running drumsd refuses new envs (409 code checkpoint_stale) until the installer re-provisions it.","responses":{"200":{"description":"OK","content":{"application\/json":{"schema":{"type":"array","items":{"$ref":"#\/components\/schemas\/Checkpoint"}}}}}}}},"\/v1\/checkpoints\/{id}":{"get":{"summary":"Get a checkpoint","description":"","responses":{"200":{"description":"OK","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Checkpoint"}}}},"404":{"description":"Error","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}]},"delete":{"summary":"Delete a saved state (or, with installed=true, an installed image checkpoint)","description":"Stops its template server and frees its images (records kept). 409 while an env made from it exists. An installed image is refused unless installed=true (the installer uses this to re-provision a stale origin).","responses":{"200":{"description":"deleted"},"409":{"description":"in use or an installed image"}},"parameters":[{"name":"installed","in":"query","required":false,"schema":{"type":"boolean","default":false}}]}},"\/v1\/slots":{"get":{"summary":"List envs (live ones by default)","description":"With no query: every live env (not destroyed), as an array (the 0.2 SDK shape). Destroyed envs keep their records and are listed only with include_destroyed=true. Any query parameter selects the paged form {items, next_after}: envs in id order (byte order) after `after`, at most `limit` (1..5000; 500 by default when include_destroyed=true, otherwise all); next_after is the id to pass as `after` for the next page, or null on the last page.","responses":{"200":{"description":"An array of envs (no query), or a page","content":{"application\/json":{"schema":{"oneOf":[{"type":"array","items":{"$ref":"#\/components\/schemas\/Slot"}},{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#\/components\/schemas\/Slot"}},"next_after":{"type":["string","null"]}},"required":["items","next_after"]}]}}}},"400":{"description":"limit out of range"}},"parameters":[{"name":"include_destroyed","in":"query","required":false,"schema":{"type":"boolean","default":false}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":5000}},{"name":"after","in":"query","required":false,"schema":{"type":"string"}}]},"post":{"summary":"Create a slot","description":"Branch a new slot from a checkpoint (hard-linked images, template-server restore) and run every synchronous gate. The alive gate resolves `alive_after_s` after the slot answered. Body: {checkpoint (alias image, from_state), task (OSWorld JSON, optional), setup (bool, optional: run the task's setup after every restore; default true for a task given here, false for a task inherited from a captured checkpoint)}.","requestBody":{"required":true,"content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/CreateRequest"}}}},"responses":{"201":{"description":"Created (check `state` and `validity`: a failed restore returns a slot in `down` or `quarantined`)","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Slot"}}}},"404":{"description":"Error","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"}}}},"500":{"description":"Error","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"}}}},"503":{"description":"Error","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"}}}}}}},"\/v1\/slots\/{id}":{"get":{"summary":"Get a slot","description":"","responses":{"200":{"description":"OK","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Slot"}}}},"404":{"description":"Error","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Slot id, e.g. s100"}]},"delete":{"summary":"Destroy a slot","description":"Kill its tree, remove its bulky state, keep its records (a full archive or, for a slot that ended valid, a summary, in forgotten_dir). Idempotent: a destroyed slot returns its record with 200. Its saved state's idle clock starts; a state made by a branch with delete_state_with_children is deleted when its last slot closes.","responses":{"200":{"description":"OK","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Slot"}}}},"404":{"description":"Error","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"}}}},"409":{"description":"Error","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"}}}},"500":{"description":"Error","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Slot id, e.g. s100"}]}},"\/v1\/slots\/{id}\/reset":{"post":{"summary":"Reset","description":"Start a new episode: kill the tree (cgroup.kill), swap in a fresh upper layer (warm spare), restore from the slot's checkpoint, run every gate. Allowed from `ready` and `down`. A failure before any task ran is retried per the recovery policy; every attempt and its cost is in `cost.attempts`.","responses":{"200":{"description":"OK","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Slot"}}}},"404":{"description":"Error","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"}}}},"409":{"description":"Error","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"}}}},"410":{"description":"env_gone: the slot was destroyed","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"}}}},"500":{"description":"Error","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Slot id, e.g. s100"}]}},"\/v1\/slots\/{id}\/step":{"post":{"summary":"Step","description":"Run one action in the slot's desktop through its guest server. Returns the action result, optionally a screenshot, and the current episode's validity.","responses":{"200":{"description":"OK","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/StepResult"}}}},"404":{"description":"Error","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"}}}},"409":{"description":"Error","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"}}}},"410":{"description":"env_gone: the slot was destroyed","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"}}}},"502":{"description":"Error","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"}}}}},"requestBody":{"required":true,"content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/StepRequest"}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Slot id, e.g. s100"}]}},"\/v1\/slots\/{id}\/screenshot":{"get":{"summary":"Screenshot","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Slot id, e.g. s100"}],"responses":{"200":{"description":"PNG","content":{"image\/png":{}}},"404":{"description":"Error","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"}}}},"410":{"description":"env_gone: the slot was destroyed","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"}}}},"502":{"description":"Error","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"}}}}}}},"\/v1\/slots\/{id}\/gates":{"get":{"summary":"Gates of the current episode","description":"","responses":{"200":{"description":"OK","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/GateView"}}}},"404":{"description":"Error","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Slot id, e.g. s100"}]}},"\/v1\/slots\/{id}\/capture":{"post":{"summary":"Capture (save)","description":"Checkpoint the running slot (`capture --leave-running`: the tree is frozen for the dump, the upper layer is copied at CRIU's post-dump action, the tree continues) and adopt it as a new checkpoint with its own template server. Capture readiness: the dump happens only after the slot's tree shows no task exiting, no unreaped zombie and no process or thread starting or exiting for the window; a dump whose CRIU-seized tree differs from that sample, or whose processes exit in the source right after, is discarded (records kept under forgotten\/) and retried. 409 with `code: not_quiescent` (and `unstable`, `quiescence`) when no good dump is possible in time: nothing is captured and the env stays ready. The checkpoint's `quiescence` field records every attempt.","responses":{"200":{"description":"OK","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Checkpoint"}}}},"404":{"description":"Error","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"}}}},"409":{"description":"Error","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"}}}},"410":{"description":"env_gone: the slot was destroyed","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"}}}},"500":{"description":"Error","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"}}}}},"requestBody":{"required":true,"content":{"application\/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"quiesce_timeout_s":{"type":"number","description":"Capture readiness: wait at most this long (s) for a quiescent tree (default from the daemon config, 30)."},"quiesce_window_s":{"type":"number","description":"Capture readiness: the quiet window (s) (default 1)."},"idle_timeout_s":{"type":"number","minimum":0,"description":"The saved state's lease (as state_idle_timeout_s in branch)."},"ttl_s":{"type":"number","minimum":0,"description":"The saved state's lease (as state_ttl_s in branch)."}}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Slot id, e.g. s100"}]}},"\/v1\/slots\/{id}\/branch":{"post":{"summary":"Branch","description":"Capture the slot's current state, then create `n` children from it concurrently. The parent keeps running its episode. Capture readiness: the dump happens only after the slot's tree shows no task exiting, no unreaped zombie and no process or thread starting or exiting for the window; a dump whose CRIU-seized tree differs from that sample, or whose processes exit in the source right after, is discarded (records kept under forgotten\/) and retried. 409 with `code: not_quiescent` (and `unstable`, `quiescence`) when no good dump is possible in time: nothing is captured and the env stays ready. The checkpoint's `quiescence` field records every attempt.","responses":{"200":{"description":"OK","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/BranchResult"}}}},"400":{"description":"Error","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"}}}},"404":{"description":"Error","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"}}}},"409":{"description":"Error","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"}}}},"410":{"description":"env_gone: the slot was destroyed","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"}}}},"500":{"description":"Error","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"}}}}},"requestBody":{"required":true,"content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/BranchRequest","properties":{"quiesce_timeout_s":{"type":"number","description":"Capture readiness: wait at most this long (s) for a quiescent tree (default from the daemon config, 30)."},"quiesce_window_s":{"type":"number","description":"Capture readiness: the quiet window (s) (default 1)."}}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Slot id, e.g. s100"}]}},"\/v1\/slots\/{id}\/quiescent":{"post":{"summary":"Wait until quiescent","description":"Block until the slot's process tree is safe to capture (no task exiting, no unreaped zombie, no process or thread starting or exiting for `window_s`). Holds the slot's operation lock while it waits. capture\/branch do this themselves; this route waits without capturing. 409 `not_quiescent` on a timeout.","responses":{"200":{"description":"The wait record: quiescent, waited_s, samples, window_s, timeout_s, restarts, processes, threads, unstable, seen, sample_ms_mean","content":{"application\/json":{"schema":{"type":"object"}}}},"404":{"description":"Error","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"}}}},"409":{"description":"Error","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"}}}},"410":{"description":"env_gone: the slot was destroyed","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"}}}},"500":{"description":"Error","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"}}}}},"requestBody":{"required":false,"content":{"application\/json":{"schema":{"type":"object","properties":{"timeout_s":{"type":"number"},"window_s":{"type":"number"}}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Slot id, e.g. s100"}]}},"\/v1\/slots\/{id}\/quarantine":{"post":{"summary":"Quarantine","description":"Stop using the slot until it is released or destroyed.","responses":{"200":{"description":"OK","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Slot"}}}},"404":{"description":"Error","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"}}}},"409":{"description":"Error","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"}}}},"410":{"description":"env_gone: the slot was destroyed","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"}}}},"500":{"description":"Error","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"}}}}},"requestBody":{"required":true,"content":{"application\/json":{"schema":{"type":"object","properties":{"reason":{"type":"string"}}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Slot id, e.g. s100"}]}},"\/v1\/slots\/{id}\/release":{"post":{"summary":"Release from quarantine","description":"Kills the quarantined tree; the slot goes to `down` and the next reset restores it.","responses":{"200":{"description":"OK","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Slot"}}}},"404":{"description":"Error","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"}}}},"409":{"description":"Error","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"}}}},"410":{"description":"env_gone: the slot was destroyed","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"}}}},"500":{"description":"Error","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Slot id, e.g. s100"}]}},"\/v1\/health":{"get":{"summary":"Health","description":"Version, CRIU binary sha256, image sha256, checkpoints with template-server liveness, slots by state, free state disk, host price, limits.","responses":{"200":{"description":"OK","content":{"application\/json":{"schema":{"type":"object"}}}}}}},"\/v1\/metrics":{"get":{"summary":"Aggregate metrics","description":"Current window: per-op latency (n, failed, mean\/p50\/p95, total s) for create\/reset\/fork\/capture\/step\/verify\/task_setup; episodes by verdict (counted once when the verdict resolves; ended_pending if ended before); failures and retries by cause; rewards (valid, flagged_invalid, checker_error); cost = host_usd_per_hour x window wall hours, per valid episode and per verified reward.","responses":{"200":{"description":"OK","content":{"application\/json":{"schema":{"type":"object"}}}}}}},"\/v1\/metrics\/reset":{"post":{"summary":"New metrics window","description":"The closed window's ledger is written to the event log.","responses":{"200":{"description":"OK","content":{"application\/json":{"schema":{"type":"object"}}}}}}},"\/v1\/tasks":{"get":{"summary":"List OSWorld tasks","description":"Tasks in the host's pinned OSWorld checkout: id, domain, instruction. Query: domain.","responses":{"200":{"description":"OK","content":{"application\/json":{"schema":{"type":"object"}}}}}}},"\/v1\/tasks\/{id}":{"get":{"summary":"Get an OSWorld task","description":"By id or unique prefix (>= 8 chars); adds _domain and _sha256 of the file.","responses":{"200":{"description":"OK","content":{"application\/json":{"schema":{"type":"object"}}}},"400":{"description":"ambiguous or short id"},"404":{"description":"no such task"}}}},"\/v1\/slots\/{id}\/verify":{"post":{"summary":"Verify (reward)","description":"Waits for the episode's pending gate, runs the task's OSWorld checker (postconfig, getters, metric) and returns {score, valid, validity, invalid_reasons, checker_skipped, gates, checker_error, state, episode, task_id, seconds}. valid is true only when every gate passed and the slot is ready (never quarantined in the episode, no OOM kill); a score from any other episode is returned with valid=false. On a quarantined slot the checker is not run: score null, checker_skipped 'quarantined: <reason>'. invalid_reasons lists why in words (empty when valid).","responses":{"200":{"description":"OK","content":{"application\/json":{"schema":{"type":"object"}}}},"400":{"description":"no task \/ not an OSWorld task"},"409":{"description":"nothing running"},"410":{"description":"env_gone: the slot was destroyed","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"}}}}}}},"\/v1\/slots\/{id}\/task":{"post":{"summary":"Load a task","description":"Run an OSWorld task's setup in this ready slot now and after every reset. On setup failure the slot is quarantined (422).","responses":{"200":{"description":"OK","content":{"application\/json":{"schema":{"type":"object"}}}},"410":{"description":"env_gone: the slot was destroyed","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"}}}},"422":{"description":"setup failed"}},"requestBody":{"content":{"application\/json":{"schema":{"type":"object","properties":{"task":{"type":"object"}},"required":["task"]}}}}}},"\/v1\/slots\/{id}\/metrics":{"get":{"summary":"Slot metrics","description":"This slot's ledger (as \/v1\/metrics), lifetime, last op timings and attempts, and its share of the host cost by slot-seconds.","responses":{"200":{"description":"OK","content":{"application\/json":{"schema":{"type":"object"}}}}}}},"\/v1\/runs":{"get":{"summary":"List runs","description":"Runs of the workspace (evaluations and scripted tests), newest first. Scope runs:read.","security":[{"bearer":[]}],"parameters":[{"name":"kind","in":"query","required":false,"description":"evaluation or scripted_test","schema":{"type":"string","enum":["evaluation","scripted_test"]}},{"name":"status","in":"query","required":false,"description":"a run status","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"1..200 (default 50)","schema":{"type":"integer","minimum":1,"maximum":200}},{"name":"cursor","in":"query","required":false,"description":"next_cursor of the previous page","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application\/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#\/components\/schemas\/Run"}},"next_cursor":{"type":"string","nullable":true}},"required":["items","next_cursor"]}}}},"403":{"description":"missing scope","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"}}}}}},"post":{"summary":"Submit a run","description":"Queue a run from a RunSpec (name, kind, task_set_id, task_ids, template_version_id, agent, execution, budget). The spec is checked against the workspace (task set, template version, secret reference, limits, remaining spend) before it is queued; the host's runner picks it up. `Idempotency-Key` header: the same key with the same body returns the same run. Scope runs:write.","security":[{"bearer":[]}],"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string","maxLength":128}}],"requestBody":{"required":true,"content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/RunSpec"}}}},"responses":{"200":{"description":"the run this Idempotency-Key already made","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/RunAccepted"}}}},"201":{"description":"queued","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/RunAccepted"}}}},"400":{"description":"invalid spec","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"}}}},"403":{"description":"missing scope, quota or budget","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"}}}},"404":{"description":"task set, task, template version or secret reference not in this workspace","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"}}}},"422":{"description":"the template version does not match the host, or the run kind is not offered","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"}}}}}}},"\/v1\/runs\/{id}":{"get":{"summary":"Get a run","description":"The run (no lease internals), an attempts summary {total, done, succeeded, verified_valid} and its last 20 events. Scope runs:read.","security":[{"bearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/RunDetail"}}}},"404":{"description":"not a run of this workspace","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"}}}}}}},"\/v1\/api-keys":{"get":{"summary":"List API keys","description":"Metadata of the workspace's keys (id, name, prefix, scopes, owner, expiry, last use, revocation). Never a secret. Any key.","security":[{"bearer":[]}],"responses":{"200":{"description":"OK","content":{"application\/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#\/components\/schemas\/ApiKeyMeta"}}},"required":["items"]}}}}}},"post":{"summary":"Create an API key","description":"Mint a key owned by the calling key's owner. Only an owner or admin key may call this; the new key's scopes cannot exceed the calling key's effective scopes. The plaintext key is in this reply once and nowhere else.","security":[{"bearer":[]}],"requestBody":{"required":true,"content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/ApiKeyCreate"}}}},"responses":{"201":{"description":"created","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/ApiKeyCreated"}}}},"400":{"description":"invalid body","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"}}}},"403":{"description":"not an owner\/admin key, or a scope the caller does not have","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"}}}}}}},"\/v1\/api-keys\/{id}":{"delete":{"summary":"Revoke an API key","description":"Revoke now; the key is refused on its next call. Only an owner or admin key; a key cannot revoke itself (400).","security":[{"bearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK","content":{"application\/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"revoked_at":{"type":"string","format":"date-time"},"already":{"type":"boolean"}},"required":["id","revoked_at"]}}}},"400":{"description":"the key tried to revoke itself","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"}}}},"403":{"description":"not an owner\/admin key","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"}}}},"404":{"description":"not a key of this workspace","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"}}}}}}},"\/v1\/audit":{"get":{"summary":"Read the audit log","description":"Audit events of the workspace, newest first. `action` filters by exact action or prefix (`mcp` matches every `mcp.<tool>` row). Scope audit:read.","security":[{"bearer":[]}],"parameters":[{"name":"action","in":"query","required":false,"description":"action or action prefix, e.g. mcp or computer.create","schema":{"type":"string","pattern":"^[a-z_]+(\\.[a-z_.]+)?$"}},{"name":"limit","in":"query","required":false,"description":"1..200 (default 50)","schema":{"type":"integer","minimum":1,"maximum":200}},{"name":"cursor","in":"query","required":false,"description":"next_cursor of the previous page","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application\/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#\/components\/schemas\/AuditEvent"}},"next_cursor":{"type":"string","nullable":true}},"required":["items","next_cursor"]}}}},"403":{"description":"missing scope","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"}}}}}}}},"components":{"schemas":{"Error":{"type":"object","properties":{"error":{"type":"string"},"code":{"type":"string","description":"Machine-readable error code when present: not_found (404), env_gone (410: the slot was destroyed; closed_reason says why when a lease did it), wrong_state (409), not_quiescent (409), checkpoint_stale (409), overloaded (429), state_dir_full (507)."},"unstable":{"type":"array"},"quiescence":{"type":"object"},"closed_reason":{"type":["string","null"]}},"required":["error"]},"AdoptRequest":{"type":"object","required":["id","slot"],"properties":{"id":{"type":"string"},"slot":{"type":"integer","description":"slot number whose directory holds images + upper.snap + records"},"expected_state_hash":{"type":"string"}}},"CreateRequest":{"type":"object","required":["checkpoint"],"properties":{"checkpoint":{"type":"string"},"task":{"$ref":"#\/components\/schemas\/Task"},"setup":{"type":"boolean","description":"Run the task's setup after every restore (default true for a task given here)."},"idle_timeout_s":{"type":"number","minimum":0,"description":"Lease: destroyed after this many seconds with no request naming it. Default: lease.env_idle_timeout_s (1800). 0 = never."},"ttl_s":{"type":"number","minimum":0,"description":"Lease: destroyed this many seconds after it was created. Default: lease.env_ttl_s (none). 0 = never."}}},"Task":{"type":"object","description":"Optional task: setup actions run after every restore, after the state-hash gate.","properties":{"id":{"type":"string"},"setup":{"type":"array","items":{"$ref":"#\/components\/schemas\/Action"}}}},"Action":{"type":"object","description":"One desktop action.","oneOf":[{"type":"object","required":["type","command"],"properties":{"type":{"const":"execute"},"command":{"description":"argv list or shell string"},"shell":{"type":"boolean"}}},{"type":"object","required":["type","code"],"properties":{"type":{"const":"pyautogui"},"code":{"type":"string","description":"python statements; `pyautogui` is imported"}}},{"type":"object","required":["type","seconds"],"properties":{"type":{"const":"sleep"},"seconds":{"type":"number","maximum":60}}}]},"StepRequest":{"type":"object","required":["action"],"properties":{"action":{"$ref":"#\/components\/schemas\/Action"},"screenshot":{"type":"boolean","default":false}}},"StepResult":{"type":"object","properties":{"result":{"type":"object","description":"guest server \/execute reply"},"screenshot_png_b64":{"type":["string","null"]},"episode":{"type":"integer"},"validity":{"$ref":"#\/components\/schemas\/Validity"},"seconds":{"type":"number"}}},"BranchRequest":{"type":"object","required":["n"],"properties":{"n":{"type":"integer","minimum":1,"maximum":64},"quiesce_timeout_s":{"type":"number"},"quiesce_window_s":{"type":"number"},"idle_timeout_s":{"type":"number","minimum":0,"description":"The children's lease (as in create)."},"ttl_s":{"type":"number","minimum":0,"description":"The children's lease (as in create)."},"state_idle_timeout_s":{"type":"number","minimum":0,"description":"The saved state's lease: deleted after this many seconds with no slot made from it (counted from its creation or its last slot's close). Default lease.state_idle_timeout_s (1800). 0 = never."},"state_ttl_s":{"type":"number","minimum":0,"description":"The saved state's lease: deleted this many seconds after it was made, once no slot uses it. Default none. 0 = never."},"delete_state_with_children":{"type":"boolean","default":false,"description":"Delete the saved state this branch makes as soon as no slot made from it exists (its children all closed). SDK: fork(keep_state=False)."}}},"BranchResult":{"type":"object","properties":{"checkpoint":{"$ref":"#\/components\/schemas\/Checkpoint"},"children":{"type":"array","items":{"$ref":"#\/components\/schemas\/Slot"}},"capture_s":{"type":"number"},"children_ready_s":{"type":"number"},"state":{"type":"string","description":"The saved state's (checkpoint's) id: the same as checkpoint.id."},"delete_state_with_children":{"type":"boolean"},"seconds":{"type":"number"}}},"Gate":{"type":"string","enum":["lock_applied","lock_pre_resume","cgroup","init_identity","mount_table","netns","guest_ready","state_hash","isolation","alive_after_resume","no_oom_kill","not_quarantined"],"description":"no_oom_kill and not_quarantined are only ever added as failed: an OOM kill during the episode, or a quarantine of the slot (detail.reason names why)."},"GateResult":{"type":"object","required":["gate","status","seconds","detail"],"properties":{"gate":{"$ref":"#\/components\/schemas\/Gate"},"status":{"type":"string","enum":["pass","fail","pending"]},"seconds":{"type":"number"},"detail":{}}},"Validity":{"type":"object","required":["verdict"],"properties":{"verdict":{"type":"string","enum":["valid","pending","invalid"]},"gates":{"type":"array","items":{"$ref":"#\/components\/schemas\/Gate"}}},"description":"valid only when every gate passed and the slot was never quarantined in the episode. A quarantined slot's validity is {verdict: invalid, gates: [..., not_quarantined]}."},"GateView":{"type":"object","properties":{"slot":{"type":"string"},"episode":{"type":"integer"},"validity":{"$ref":"#\/components\/schemas\/Validity"},"gates":{"type":"object","properties":{"gates":{"type":"array","items":{"$ref":"#\/components\/schemas\/GateResult"}}}},"cost":{"$ref":"#\/components\/schemas\/Cost"}}},"Attempt":{"type":"object","properties":{"n":{"type":"integer"},"mode":{"type":"string","enum":["template","private"]},"seconds":{"type":"number"},"ok":{"type":"boolean"},"class":{"type":"string","enum":["detach_race","template_refused","template_server_down","restore_failed","gate_failed","infra"]},"error":{"type":"string"},"action":{"type":"string","enum":["retry","retry_private","restart_server_and_retry","give_up"]}}},"Cost":{"type":"object","properties":{"attempts":{"type":"array","items":{"$ref":"#\/components\/schemas\/Attempt"}}}},"Slot":{"type":"object","properties":{"id":{"type":"string"},"num":{"type":"integer"},"checkpoint":{"type":"string"},"state":{"type":"string","enum":["creating","ready","resetting","capturing","down","quarantined","destroying","destroyed"]},"reason":{"type":"string","description":"for down and quarantined"},"episode":{"type":"integer"},"init_pid":{"type":["integer","null"]},"init_start_ticks":{"type":["string","null"]},"restore_generation":{"type":"integer"},"mode":{"type":"string"},"port":{"type":"integer"},"url":{"type":"string"},"extra_ports":{"type":"array","items":{"type":"array","items":{"type":"integer"}}},"gates":{"type":"object","properties":{"gates":{"type":"array","items":{"$ref":"#\/components\/schemas\/GateResult"}}}},"validity":{"$ref":"#\/components\/schemas\/Validity"},"cost":{"$ref":"#\/components\/schemas\/Cost"},"timings":{"type":"object","additionalProperties":{"type":"number"},"description":"seconds per phase: kill, upper, prepare, restore, criu_clock, gates_kernel, guest_ready, ready, hash_isolation, task_setup, sync_total, total"},"last_error":{"type":["string","null"]},"task":{},"ready_epoch":{"type":["number","null"]},"created_epoch":{"type":"number"},"updated_epoch":{"type":"number"},"lease":{"type":["object","null"],"properties":{"idle_timeout_s":{"type":["number","null"]},"ttl_s":{"type":["number","null"]}}},"last_used_epoch":{"type":["number","null"],"description":"The last request naming this slot."},"lease_expires_epoch":{"type":["number","null"],"description":"When the lease runs out if the slot is not used again (null: no lease, or destroyed)."},"closed_reason":{"type":["string","null"],"description":"Why it was destroyed when not by a client's DELETE, e.g. 'lease: idle 1800 s'."},"closed_epoch":{"type":["number","null"]}}},"Checkpoint":{"type":"object","properties":{"id":{"type":"string"},"slot":{"type":"integer"},"socket":{"type":"string"},"server_pid":{"type":["integer","null"]},"argv_marker":{"type":"string"},"expected_state_hash":{"type":["string","null"]},"hash_reference":{"type":"string"},"captured_from":{"type":["string","null"]},"created_epoch":{"type":"number"},"server_ready_s":{"type":"number"},"quiescence":{"type":["object","null"],"description":"Capture readiness record: params, every attempt (wait, dump, checks, why a rejected dump was rejected)."}}},"RunSpec":{"type":"object","description":"The run configuration the console's New run form submits (packages\/contracts runSpecSchema).","properties":{"name":{"type":"string","pattern":"^[a-z0-9][a-z0-9-]{0,63}$"},"kind":{"type":"string","enum":["evaluation","scripted_test"]},"task_set_id":{"type":"string","format":"uuid"},"task_ids":{"type":"array","items":{"type":"string"},"minItems":1},"template_version_id":{"type":"string","format":"uuid"},"agent":{"type":"object","description":"{kind: scripted} or {kind: model, endpoint_url, model, secret_ref_id, max_tokens?, temperature?}"},"execution":{"type":"object","properties":{"parallelism":{"type":"integer"},"fork_n":{"type":"integer"},"prefix_steps":{"type":"integer"},"max_steps":{"type":"integer"},"rounds":{"type":"integer"}}},"budget":{"type":"object","properties":{"usd":{"type":"number"},"max_duration_s":{"type":"integer"},"verified_target":{"type":"integer"}}},"retry_of_run_id":{"type":"string","format":"uuid"}},"required":["name","kind","task_set_id","task_ids","template_version_id","agent","execution","budget"]},"RunAccepted":{"type":"object","properties":{"run_id":{"type":"string","format":"uuid"},"operation_id":{"type":"string","format":"uuid"},"status":{"type":"string"},"created":{"type":"boolean"}},"required":["run_id","operation_id","status","created"]},"Run":{"type":"object","description":"A runs row without the runner lease (leased_by, lease_until).","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"kind":{"type":"string"},"status":{"type":"string"},"stop_reason":{"type":"string","nullable":true},"agent_kind":{"type":"string"},"model_ref":{"type":"object","nullable":true},"task_set_id":{"type":"string"},"template_version_id":{"type":"string"},"config":{"type":"object"},"config_hash":{"type":"string"},"budget_usd":{"type":"number"},"max_duration_s":{"type":"integer"},"parallelism":{"type":"integer"},"cost_to_date_usd":{"type":"number"},"attempts_total":{"type":"integer"},"attempts_valid":{"type":"integer"},"attempts_success":{"type":"integer"},"error_code":{"type":"string","nullable":true},"error_message":{"type":"string","nullable":true},"created_by":{"type":"string"},"created_via":{"type":"string"},"queued_at":{"type":"string","nullable":true},"started_at":{"type":"string","nullable":true},"ended_at":{"type":"string","nullable":true},"created_at":{"type":"string"}}},"RunDetail":{"allOf":[{"$ref":"#\/components\/schemas\/Run"},{"type":"object","properties":{"attempts":{"type":"object","properties":{"total":{"type":"integer"},"done":{"type":"integer"},"succeeded":{"type":"integer"},"verified_valid":{"type":"integer"}}},"events":{"type":"array","items":{"type":"object","properties":{"seq":{"type":"integer"},"type":{"type":"string"},"event_at":{"type":"string"},"attempt_id":{"type":"string","nullable":true},"payload":{"type":"object"}}}}}}]},"ApiKeyCreate":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":80},"scopes":{"type":"array","items":{"type":"string","enum":["computers:read","computers:write","runs:read","runs:write","templates:read","templates:write","usage:read","audit:read"]},"minItems":1},"expires_in_days":{"type":"integer","minimum":1,"maximum":365}},"required":["name","scopes"]},"ApiKeyCreated":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"prefix":{"type":"string"},"key":{"type":"string","description":"the plaintext key, shown once"},"expires_at":{"type":"string","nullable":true}},"required":["id","prefix","key","expires_at"]},"ApiKeyMeta":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"prefix":{"type":"string"},"scopes":{"type":"array","items":{"type":"string"}},"owner_user_id":{"type":"string"},"expires_at":{"type":"string","nullable":true},"last_used_at":{"type":"string","nullable":true},"revoked_at":{"type":"string","nullable":true},"rotated_from":{"type":"string","nullable":true},"created_at":{"type":"string"}}},"AuditEvent":{"type":"object","properties":{"id":{"type":"integer"},"actor_user_id":{"type":"string","nullable":true},"actor_kind":{"type":"string","enum":["user","api_key","operator","system","mcp"]},"actor_api_key_id":{"type":"string","nullable":true},"action":{"type":"string"},"target_type":{"type":"string","nullable":true},"target_id":{"type":"string","nullable":true},"outcome":{"type":"string","enum":["success","denied","failure"]},"error_code":{"type":"string","nullable":true},"request_id":{"type":"string"},"origin":{"type":"string","nullable":true},"source_ip_redacted":{"type":"string","nullable":true},"metadata":{"type":"object"},"created_at":{"type":"string"}}}},"securitySchemes":{"bearer":{"type":"http","scheme":"bearer"}}},"security":[{"bearer":[]}],"servers":[{"url":"\/gw","description":"Drums Cloud gateway (Bearer: a Drums API key)"}]}