{
  "openapi": "3.1.0",
  "info": {
    "title": "offstage cloud API",
    "version": "0.1.0",
    "summary": "Real Macs for AI agents and CI, over HTTP.",
    "description": "macOS sandboxes driven one call at a time, and fire-and-forget CI tasks, on Apple silicon Macs running macOS 27.\n\n- **Auth.** `Authorization: Bearer sk_offstage_\u2026` (a team API key; one made before 2026-10-29 starts with `oc_live_` and still works), or the console's `oc_session` cookie. Operations marked `x-console-only: true` take only the cookie; an API key gets 403 `console-only`. A cookie request that changes something must come from this origin.\n- **Errors.** Every failure is `{\"error\": {\"code\", \"message\", \"fix\"?, \"requestId\"}}`. Switch on `code`; `fix`, when present, says what to do. Any operation can also answer 500 `internal` (a bug; quote the `requestId`), and an unknown route is 404 `no-such-route`.\n- **Request ids.** Every response carries an `x-request-id` header, repeated as `requestId` in error bodies.\n- **Units.** Times are Unix milliseconds, and durations end in `Ms`. Money is US dollars as numbers, exact to the micro-dollar (`costUsd`).\n- **Ids** are prefixed: `sbx_` sandbox, `cmd_` command, `run_` agent run, `task_` task, `key_` API key, `req_` request, `team_`, `usr_`, `box_` (a fleet Mac), `inv_`.\n- **Lists** are newest first. Pass the previous page's `nextCursor` as `cursor`; it is null on the last page.\n- **Bodies.** JSON, except file writes, which send raw bytes. Request schemas with `additionalProperties: false` refuse an unknown field with 400 `unknown-field`.\n- **Limits.** A team may send 120 requests a minute, whatever key or session it uses. Every request also counts against its client address: 600 a minute, and far fewer on the routes that need no credential (5 sign-ups an hour, 20 sign-ins and 20 verifications a minute). Over a window is 429 `rate-limit` with a `Retry-After` header.\n- **Verification.** A new account's team can sign in and look around, but cannot create sandboxes, tasks or API keys (403 `email-unverified`) until its owner opens the link emailed at signup.\n- **Holds.** No request is held longer than about 50 s, so default client timeouts and 60 s proxy idle timeouts are fine.\n\nThe prose reference, with examples, is `https://api.offstage.cloud/llms.txt`. The agent setup guide is `https://api.offstage.cloud/SKILL.md`."
  },
  "servers": [
    {
      "url": "https://api.offstage.cloud",
      "description": "This plane: the origin this document was served from."
    }
  ],
  "security": [
    {
      "apiKey": []
    },
    {
      "consoleSession": []
    }
  ],
  "tags": [
    {
      "name": "Sandboxes",
      "description": "A Mac slot (a macOS user account) held for you: run commands, move files, take screenshots."
    },
    {
      "name": "Tasks",
      "description": "Fire-and-forget macOS work: clone a public repo, run a command, collect artifacts."
    },
    {
      "name": "Recordings",
      "description": "What a gui sandbox or gui task showed and did, kept by the plane: live while it runs, a replay after. Open one in the console at `/r/{id}`."
    },
    {
      "name": "Capacity",
      "description": "What the fleet can take right now, and whether the plane is up."
    },
    {
      "name": "Billing",
      "description": "The price, usage, the bill, the budget and the card."
    },
    {
      "name": "Account",
      "description": "Signing in, the team, its members and its API keys."
    },
    {
      "name": "GitHub",
      "description": "GitHub Actions jobs on offstage Macs: connect an organization in the console, then `runs-on: offstage-macos`."
    }
  ],
  "paths": {
    "/v1/tasks": {
      "post": {
        "operationId": "createTask",
        "tags": [
          "Tasks"
        ],
        "summary": "Submit a task",
        "description": "Queues fire-and-forget macOS work: a Mac slot clones `repo` (`git clone --depth 1` of the default branch), runs `command` (or the `agent` program, for `kind: agent`) as `zsh -lc` in the clone with `CI=1`, uploads the promised `artifacts`, and reports the outcome. The task's worst case (`timeoutMs` at the hourly price) is reserved against the credit and the budget until it ends; time spent queued is free. A team may have at most 16 tasks waiting and 4 claimed or running at once; free slots go first to the team with the fewest tasks running, so one team's backlog never holds another's work behind it. A finished task is kept for 30 days (fewer when the team's finished tasks pass 4 MiB of records, oldest first), then `GET /v1/tasks/{taskId}` is 404.",
        "security": [
          {
            "apiKey": []
          },
          {
            "consoleSession": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateTaskRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The task, in state `queued`. A replay of an earlier create with the same `Idempotency-Key` and body answers `201` with that task as it is now (the same `id`, in its current state), with `idempotent-replayed: true`.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              },
              "idempotent-replayed": {
                "$ref": "#/components/headers/IdempotentReplayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TaskView"
                }
              }
            }
          },
          "400": {
            "description": "A field is wrong; the message names it. `not-an-object`, `unknown-field` (the `fix` lists the fields), `bad-kind`, `missing-repo`, `bad-repo` (not https, carries credentials, or over 2048 characters), `missing-command`, `no-agent-on-command`, `missing-agent`, `no-command-on-agent`, `bad-session`, `bad-timeout`, `bad-artifacts`, `bad-artifact-name`, `bad-idempotency-key`.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized`: no credential, an `Authorization` header that is not `Bearer \u2026`, a value that is not an `sk_offstage_` (or older `oc_live_`) key, a revoked or unknown key, or a box token. The message says which.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "The work's worst case (its `timeoutMs` at the hourly price) does not fit. `payment-method-required`: the team has no card and the free credit, after the work already in flight, cannot cover it; add a card or ask for a shorter `timeoutMs`. `budget-exceeded`: this month's spend plus the work in flight plus this request could pass the enforced monthly budget; raise or lift it, or ask for a shorter `timeoutMs`. `credit-ceiling`: the team has a card, and what it owes beyond the free credit (unpaid invoices, usage not invoiced yet, the work in flight) plus this request could pass the credit ceiling offstage sets; the budget cannot lift it. `payment-failed`: the last invoice's charge failed and new work is paused until the card is fixed.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`forbidden`: a cookie-authenticated request that changes something came from another origin. `email-unverified`: the team's owner has not verified their email yet; open the link emailed at signup, or send a new one.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "`gui-paused`: GUI work is paused for customer teams while the fleet takes a security fix; use `\"session\": \"headless\"` if the work needs no screen. `no-capacity`: Macs are online but no slot takes this `session` right now (usually `gui`); see `GET /v1/capacity`. If no Mac is online at all the task queues instead. `idempotency-key-reused`: this key was sent with a different body. `idempotency-key-in-use`: the first request with this key is still being handled; retry shortly. `idempotency-key-spent`: this key made a task that is no longer kept; use a new key.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "`rate-limit`: this team has sent more than 120 requests in the last 60 s, or this client address more than 600; `Retry-After` gives the seconds left in the window. `task-backlog`: the team already has 16 tasks waiting for a Mac; submit more as they start, or cancel ones you no longer need. `queue-full`: the plane already has 500 tasks waiting across every team; `Retry-After` says when to try again.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              },
              "Retry-After": {
                "schema": {
                  "type": "string"
                },
                "description": "Seconds to wait before sending it again, for `rate-limit` and `queue-full`."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "$ref": "#/components/responses/BodyTooLarge"
          },
          "408": {
            "description": "`body-timeout`: the request body did not arrive within 30 s. The connection is closed; send it again.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "get": {
        "operationId": "listTasks",
        "tags": [
          "Tasks"
        ],
        "summary": "List tasks",
        "description": "Lists the team's tasks, newest first, as summaries without `events` (`GET /v1/tasks/{taskId}` has them). Filter by `state` and `kind`; page with `limit` and `cursor`.",
        "security": [
          {
            "apiKey": []
          },
          {
            "consoleSession": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/TaskState"
          },
          {
            "$ref": "#/components/parameters/TaskKind"
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of task summaries.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TaskList"
                }
              }
            }
          },
          "400": {
            "description": "`bad-state`, `bad-kind`, `bad-limit`, or `bad-cursor` (the cursor names no task of yours; start again without one).",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized`: no credential, an `Authorization` header that is not `Bearer \u2026`, a value that is not an `sk_offstage_` (or older `oc_live_`) key, a revoked or unknown key, or a box token. The message says which.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "413": {
            "$ref": "#/components/responses/BodyTooLarge"
          }
        }
      }
    },
    "/v1/tasks/{taskId}": {
      "get": {
        "operationId": "getTask",
        "tags": [
          "Tasks"
        ],
        "summary": "Get a task",
        "description": "Returns a task with its event log. With `wait`, holds the request until the task reaches a terminal state or `wait` seconds pass (never more than 50 s), so calling `?wait=50` in a loop follows a task with about one request a minute.",
        "security": [
          {
            "apiKey": []
          },
          {
            "consoleSession": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/TaskId"
          },
          {
            "$ref": "#/components/parameters/Wait"
          }
        ],
        "responses": {
          "200": {
            "description": "The task.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TaskView"
                }
              }
            }
          },
          "400": {
            "description": "`bad-wait`: `wait` must be a number of seconds from 0 to 60.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized`: no credential, an `Authorization` header that is not `Bearer \u2026`, a value that is not an `sk_offstage_` (or older `oc_live_`) key, a revoked or unknown key, or a box token. The message says which.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "`not-found`: no such task, or it belongs to another team, or it finished more than 30 days ago (or is among its team's oldest finished tasks past 4 MiB of records) and was forgotten.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/tasks/{taskId}/cancel": {
      "post": {
        "operationId": "cancelTask",
        "tags": [
          "Tasks"
        ],
        "summary": "Cancel a task",
        "description": "Ends a task that has not ended yet and returns it in state `cancelled`; no body is needed. A queued task never runs and costs nothing; a claimed or running one is stopped on its Mac within about 15 s and billed up to the cancel, and artifacts it had not uploaded are lost.",
        "security": [
          {
            "apiKey": []
          },
          {
            "consoleSession": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/TaskId"
          }
        ],
        "responses": {
          "200": {
            "description": "The task, now `cancelled`.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TaskView"
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized`: no credential, an `Authorization` header that is not `Bearer \u2026`, a value that is not an `sk_offstage_` (or older `oc_live_`) key, a revoked or unknown key, or a box token. The message says which.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`forbidden`: a cookie-authenticated request that changes something came from another origin.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "`not-found`: no such task, or it belongs to another team.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "`already-ended`: the task had already ended; the message gives its final state.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/tasks/{taskId}/artifacts/{name}": {
      "get": {
        "operationId": "getTaskArtifact",
        "tags": [
          "Tasks"
        ],
        "summary": "Download an artifact",
        "description": "Returns an artifact the task's Mac uploaded, as its raw bytes with the content type it was uploaded with, served as a download. It is 404 until the Mac uploads it, for a name the task did not promise, and once it is deleted, 7 days after its task ends.",
        "security": [
          {
            "apiKey": []
          },
          {
            "consoleSession": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/TaskId"
          },
          {
            "$ref": "#/components/parameters/ArtifactName"
          }
        ],
        "responses": {
          "200": {
            "description": "The artifact's bytes. `content-type` is whatever the Mac uploaded it as.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              },
              "content-disposition": {
                "description": "`attachment; filename=\"<name>\"`.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "*/*": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized`: no credential, an `Authorization` header that is not `Bearer \u2026`, a value that is not an `sk_offstage_` (or older `oc_live_`) key, a revoked or unknown key, or a box token. The message says which.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "`not-found`: no such task (or it belongs to another team), no such artifact yet, or an artifact deleted 7 days after its task ended (the message says so).",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "`bad-artifact-name`: the name could never be an artifact (see the artifact name rules on `CreateTaskRequest.artifacts`).",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/sandboxes": {
      "post": {
        "operationId": "createSandbox",
        "tags": [
          "Sandboxes"
        ],
        "summary": "Create a sandbox",
        "description": "Creates a sandbox: one macOS user account on a fleet Mac, held for you until it closes. It is `pending` until a slot takes it and then `ready`, and bills per second from `ready` until it closes. An empty body gives a default headless sandbox; its worst case (`timeoutMs` at the hourly price) is reserved against the credit and the budget.",
        "security": [
          {
            "apiKey": []
          },
          {
            "consoleSession": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": false,
          "description": "Optional: an empty body is a default sandbox.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateSandboxRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The sandbox, usually `pending`. A replay with the same `Idempotency-Key` and body answers `201` with that sandbox as it is now (the same `id`, in its current state), with `idempotent-replayed: true`.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              },
              "idempotent-replayed": {
                "$ref": "#/components/headers/IdempotentReplayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Sandbox"
                }
              }
            }
          },
          "400": {
            "description": "`not-an-object` (a body that is not a JSON object), `unknown-field`, `bad-session`, `bad-timeout`, `bad-name`, `bad-idempotency-key`.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized`: no credential, an `Authorization` header that is not `Bearer \u2026`, a value that is not an `sk_offstage_` (or older `oc_live_`) key, a revoked or unknown key, or a box token. The message says which.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "The work's worst case (its `timeoutMs` at the hourly price) does not fit. `payment-method-required`: the team has no card and the free credit, after the work already in flight, cannot cover it; add a card or ask for a shorter `timeoutMs`. `budget-exceeded`: this month's spend plus the work in flight plus this request could pass the enforced monthly budget; raise or lift it, or ask for a shorter `timeoutMs`. `credit-ceiling`: the team has a card, and what it owes beyond the free credit (unpaid invoices, usage not invoiced yet, the work in flight) plus this request could pass the credit ceiling offstage sets; the budget cannot lift it. `payment-failed`: the last invoice's charge failed and new work is paused until the card is fixed.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`forbidden`: a cookie-authenticated request that changes something came from another origin, or a console session that no longer acts for the team the request began on (its user left that team while the body arrived). `email-unverified`: the team's owner has not verified their email yet; open the link emailed at signup, or send a new one.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "`gui-paused`: GUI work is paused for customer teams while the fleet takes a security fix; use `\"session\": \"headless\"` if the work needs no screen. `no-capacity`: Macs are online but no slot takes this `session` right now; see `GET /v1/capacity`. `name-taken`: an open sandbox of the team already has that `name`. `idempotency-key-reused`, `idempotency-key-in-use`: as on `createTask`.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "`concurrency-limit`: the team already holds its limit of open (pending or ready) sandboxes. Kill one you no longer need.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "$ref": "#/components/responses/BodyTooLarge"
          }
        }
      },
      "get": {
        "operationId": "listSandboxes",
        "tags": [
          "Sandboxes"
        ],
        "summary": "List sandboxes",
        "description": "Lists the team's sandboxes, newest first. Filter by `state`; page with `limit` and `cursor`.",
        "security": [
          {
            "apiKey": []
          },
          {
            "consoleSession": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/SandboxState"
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of sandboxes.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SandboxList"
                }
              }
            }
          },
          "400": {
            "description": "`bad-state`, `bad-limit`, or `bad-cursor`.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized`: no credential, an `Authorization` header that is not `Bearer \u2026`, a value that is not an `sk_offstage_` (or older `oc_live_`) key, a revoked or unknown key, or a box token. The message says which.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "413": {
            "$ref": "#/components/responses/BodyTooLarge"
          }
        }
      }
    },
    "/v1/sandboxes/{sandboxId}": {
      "get": {
        "operationId": "getSandbox",
        "tags": [
          "Sandboxes"
        ],
        "summary": "Get a sandbox",
        "description": "Returns a sandbox: its state, its Mac and slot, when it expires, and what it has cost so far.",
        "security": [
          {
            "apiKey": []
          },
          {
            "consoleSession": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/SandboxId"
          }
        ],
        "responses": {
          "200": {
            "description": "The sandbox.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Sandbox"
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized`: no credential, an `Authorization` header that is not `Bearer \u2026`, a value that is not an `sk_offstage_` (or older `oc_live_`) key, a revoked or unknown key, or a box token. The message says which.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "`not-found`: no such sandbox, or it belongs to another team.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "patch": {
        "operationId": "renameSandbox",
        "tags": [
          "Sandboxes"
        ],
        "summary": "Rename a sandbox",
        "description": "Changes the sandbox's `name` and returns it. Only the name: its lifetime has `POST /v1/sandboxes/{sandboxId}/timeout`. A closed sandbox can be renamed too.",
        "security": [
          {
            "apiKey": []
          },
          {
            "consoleSession": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/SandboxId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RenameSandboxRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The sandbox, with its new `name`.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Sandbox"
                }
              }
            }
          },
          "400": {
            "description": "`not-an-object`, `unknown-field`, or `bad-name` (missing, or not 1 to 48 lowercase letters, digits and inner hyphens).",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized`: no credential, an `Authorization` header that is not `Bearer \u2026`, a value that is not an `sk_offstage_` (or older `oc_live_`) key, a revoked or unknown key, or a box token. The message says which.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`forbidden`: a cookie-authenticated request that changes something came from another origin.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "`not-found`: no such sandbox, or it belongs to another team.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "`name-taken`: another open sandbox of the team already has that name.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "413": {
            "$ref": "#/components/responses/BodyTooLarge"
          }
        }
      },
      "delete": {
        "operationId": "killSandbox",
        "tags": [
          "Sandboxes"
        ],
        "summary": "Kill a sandbox",
        "description": "Closes the sandbox with `closeReason: deleted`: the Mac kills everything still running in it, deletes its root and frees the slot, and billing stops. Killing a closed sandbox is a no-op that returns it.",
        "security": [
          {
            "apiKey": []
          },
          {
            "consoleSession": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/SandboxId"
          }
        ],
        "responses": {
          "200": {
            "description": "The sandbox, now `closed`.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Sandbox"
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized`: no credential, an `Authorization` header that is not `Bearer \u2026`, a value that is not an `sk_offstage_` (or older `oc_live_`) key, a revoked or unknown key, or a box token. The message says which.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`forbidden`: a cookie-authenticated request that changes something came from another origin.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "`not-found`: no such sandbox, or it belongs to another team.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/sandboxes/{sandboxId}/timeout": {
      "post": {
        "operationId": "setSandboxTimeout",
        "tags": [
          "Sandboxes"
        ],
        "summary": "Set the timeout",
        "description": "Makes the sandbox close `timeoutMs` from now, sooner or later than before, like E2B's `setTimeout`, and returns it; the answer's `timeoutMs` is the new lifetime counted from creation, and `idleTimeoutMs` is lowered to it if it was larger. A sandbox lives at most 24 hours from its creation, and lengthening it reserves the extra time against the credit and the budget. A command still waiting for a place in the sandbox runs for the new life, up to its own `timeoutMs`; one already running keeps the end it started with.",
        "security": [
          {
            "apiKey": []
          },
          {
            "consoleSession": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/SandboxId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SetTimeoutRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The sandbox, with its new `timeoutMs` and `expiresAt`.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Sandbox"
                }
              }
            }
          },
          "400": {
            "description": "`not-an-object`, `unknown-field`, or `bad-timeout` (missing, not a positive integer, or past 24 hours from creation; the message says how much time is left).",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized`: no credential, an `Authorization` header that is not `Bearer \u2026`, a value that is not an `sk_offstage_` (or older `oc_live_`) key, a revoked or unknown key, or a box token. The message says which.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "The work's worst case (its `timeoutMs` at the hourly price) does not fit. `payment-method-required`: the team has no card and the free credit, after the work already in flight, cannot cover it; add a card or ask for a shorter `timeoutMs`. `budget-exceeded`: this month's spend plus the work in flight plus this request could pass the enforced monthly budget; raise or lift it, or ask for a shorter `timeoutMs`. `credit-ceiling`: the team has a card, and what it owes beyond the free credit (unpaid invoices, usage not invoiced yet, the work in flight) plus this request could pass the credit ceiling offstage sets; the budget cannot lift it. `payment-failed`: the last invoice's charge failed and new work is paused until the card is fixed.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`forbidden`: a cookie-authenticated request that changes something came from another origin.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "`not-found`: no such sandbox, or it belongs to another team.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "`sandbox-closed`: the sandbox has already closed.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "413": {
            "$ref": "#/components/responses/BodyTooLarge"
          }
        }
      }
    },
    "/v1/sandboxes/{sandboxId}/commands": {
      "post": {
        "operationId": "runCommand",
        "tags": [
          "Sandboxes"
        ],
        "summary": "Run a command",
        "description": "Runs a shell command line as `zsh -lc` in the sandbox, as the slot's macOS user. The answer comes within about 50 s whatever the command does: 200 with the result if it ended by then, else 202 with a `commandId` to follow with `getCommand`. A non-zero exit is still 200, so check `exitCode`; on a `pending` sandbox the command first waits up to 30 s for a slot. Starting a command is not idempotent.",
        "security": [
          {
            "apiKey": []
          },
          {
            "consoleSession": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/SandboxId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RunCommandRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The command ended within the hold.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CommandResult"
                }
              }
            }
          },
          "202": {
            "description": "The command is still running. Follow it with `GET /v1/sandboxes/{sandboxId}/commands/{commandId}?wait=50`.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CommandRunning"
                }
              }
            }
          },
          "400": {
            "description": "`not-an-object`, `unknown-field` (the fields are `cmd`, `cwd`, `envs`, `timeoutMs`), `missing-cmd`, `bad-cmd` (over 64 KiB), `bad-path` (a `cwd` the plane or the Mac refused), `bad-envs`, `bad-timeout`.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized`: no credential, an `Authorization` header that is not `Bearer \u2026`, a value that is not an `sk_offstage_` (or older `oc_live_`) key, a revoked or unknown key, or a box token. The message says which.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`forbidden`: a cookie-authenticated request that changes something came from another origin.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "`not-found`: no such sandbox, or it belongs to another team.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "`sandbox-not-ready`: the sandbox was still `pending` after waiting 30 s for a slot; the op never ran, so retry, or GET the sandbox until it is `ready`. `sandbox-closed`: the sandbox has closed; the message gives the reason. `op-failed`: the Mac reported an error; the message starts with the Mac's own code. `sandbox-busy` means 16 ops were running in the sandbox and 16 more waiting for a place, so this one was not taken: retry once some end.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "504": {
            "description": "`op-timeout`: the Mac did not answer within the op's deadline (a command's `timeoutMs` plus 30 s; 90 s for a file or a screenshot), counted from when it took the op; or no place freed in the sandbox for the op within that deadline, and it never ran. GET the sandbox: if it closed as `box-lost`, start over; otherwise retry.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "413": {
            "$ref": "#/components/responses/BodyTooLarge"
          }
        }
      }
    },
    "/v1/sandboxes/{sandboxId}/commands/{commandId}": {
      "get": {
        "operationId": "getCommand",
        "tags": [
          "Sandboxes"
        ],
        "summary": "Follow a command",
        "description": "Follows a command that answered 202. Holds until the command ends or `wait` seconds pass (never more than 50 s), then answers 200 with the result, 202 while it still runs, or the command's own error with the status it would have had. A finished command's answer stays readable for 10 minutes, so a lost follow can be repeated, within caps: a team keeps at most 500 answers and 16 MiB of them, and the plane 10,000 and 128 MiB across teams. Past a cap the team's own oldest answers go first, never another team's; an answer that cannot fit is not kept, though the request waiting on it still gets it. Closing a sandbox drops its commands. Running commands are forgotten if the plane restarts.",
        "security": [
          {
            "apiKey": []
          },
          {
            "consoleSession": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/SandboxId"
          },
          {
            "$ref": "#/components/parameters/CommandId"
          },
          {
            "$ref": "#/components/parameters/Wait"
          }
        ],
        "responses": {
          "200": {
            "description": "The command has ended.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CommandResult"
                }
              }
            }
          },
          "202": {
            "description": "The command is still running; follow again.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CommandRunning"
                }
              }
            }
          },
          "400": {
            "description": "`bad-wait`: `wait` must be a number of seconds from 0 to 60. Or the command's own `bad-path` from the Mac.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized`: no credential, an `Authorization` header that is not `Bearer \u2026`, a value that is not an `sk_offstage_` (or older `oc_live_`) key, a revoked or unknown key, or a box token. The message says which.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "`not-found`: no such sandbox, or no such command in it (never started, finished more than 10 minutes ago, pushed out by the team's newer answers or not kept under the caps, or forgotten in a plane restart).",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "`sandbox-closed`: the sandbox has closed, and its commands went with it; or the command's own error, `sandbox-closed` (the sandbox closed under it) or `op-failed`.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "504": {
            "description": "`op-timeout`: the Mac did not answer within the op's deadline (a command's `timeoutMs` plus 30 s; 90 s for a file or a screenshot), counted from when it took the op; or no place freed in the sandbox for the op within that deadline, and it never ran. GET the sandbox: if it closed as `box-lost`, start over; otherwise retry.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/sandboxes/{sandboxId}/files": {
      "put": {
        "operationId": "writeFile",
        "tags": [
          "Sandboxes"
        ],
        "summary": "Write a file",
        "description": "Writes the raw request body to `path` under the sandbox root, creating its directories and replacing any existing file (it never appends). The limit is 10 MiB, refused on the declared `content-length` before any upload. A team may have 24 MiB of file writes arriving at once; a write past that is 429 `uploads-busy`.",
        "security": [
          {
            "apiKey": []
          },
          {
            "consoleSession": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/SandboxId"
          },
          {
            "$ref": "#/components/parameters/FilePath"
          }
        ],
        "requestBody": {
          "required": false,
          "description": "The file's bytes, sent as-is. Any `content-type` is accepted; an empty body writes an empty file.",
          "content": {
            "application/octet-stream": {
              "schema": {
                "type": "string",
                "format": "binary"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The file was written.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FileWritten"
                }
              }
            }
          },
          "400": {
            "description": "`bad-path`: `path` is missing, absolute, contains `..`, or uses characters other than letters, digits, `.`, `_`, `-` and `/`.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized`: no credential, an `Authorization` header that is not `Bearer \u2026`, a value that is not an `sk_offstage_` (or older `oc_live_`) key, a revoked or unknown key, or a box token. The message says which.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`forbidden`: a cookie-authenticated request that changes something came from another origin.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "`not-found`: no such sandbox, or it belongs to another team.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "`sandbox-not-ready`: the sandbox was still `pending` after waiting 30 s for a slot; the op never ran, so retry, or GET the sandbox until it is `ready`. `sandbox-closed`: the sandbox has closed; the message gives the reason. `op-failed`: the Mac reported an error; the message starts with the Mac's own code. `sandbox-busy` means 16 ops were running in the sandbox and 16 more waiting for a place, so this one was not taken: retry once some end.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "`file-too-large`: the body is over 10 MiB (10,485,760 bytes).",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "504": {
            "description": "`op-timeout`: the Mac did not answer within the op's deadline (a command's `timeoutMs` plus 30 s; 90 s for a file or a screenshot), counted from when it took the op; or no place freed in the sandbox for the op within that deadline, and it never ran. GET the sandbox: if it closed as `box-lost`, start over; otherwise retry.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "`rate-limit`: this team has sent more than 120 requests in the last 60 s, or this client address more than 600; `Retry-After` gives the seconds left in the window. `uploads-busy`: the plane is already taking in as many file writes as it holds at once, from this team (24 MiB of them) or from every team together; the message says which. Send it again once the team's other writes finish.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              },
              "Retry-After": {
                "schema": {
                  "type": "string"
                },
                "description": "Seconds to wait before sending it again: the seconds left in the window for `rate-limit`, 1 for `uploads-busy`."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "get": {
        "operationId": "readFile",
        "tags": [
          "Sandboxes"
        ],
        "summary": "Read a file",
        "description": "Returns the bytes of the file at `path` under the sandbox root, exactly, as `application/octet-stream`.",
        "security": [
          {
            "apiKey": []
          },
          {
            "consoleSession": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/SandboxId"
          },
          {
            "$ref": "#/components/parameters/FilePath"
          }
        ],
        "responses": {
          "200": {
            "description": "The file's bytes.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/octet-stream": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "400": {
            "description": "`bad-path`: `path` is missing or breaks the path rules.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized`: no credential, an `Authorization` header that is not `Bearer \u2026`, a value that is not an `sk_offstage_` (or older `oc_live_`) key, a revoked or unknown key, or a box token. The message says which.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "`not-found`: no such sandbox, or it belongs to another team. `file-not-found`: `path` is not a file.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "`sandbox-not-ready`: the sandbox was still `pending` after waiting 30 s for a slot; the op never ran, so retry, or GET the sandbox until it is `ready`. `sandbox-closed`: the sandbox has closed; the message gives the reason. `op-failed`: the Mac reported an error; the message starts with the Mac's own code. `sandbox-busy` means 16 ops were running in the sandbox and 16 more waiting for a place, so this one was not taken: retry once some end.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "`file-too-large`: the file is over 10 MiB.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "504": {
            "description": "`op-timeout`: the Mac did not answer within the op's deadline (a command's `timeoutMs` plus 30 s; 90 s for a file or a screenshot), counted from when it took the op; or no place freed in the sandbox for the op within that deadline, and it never ran. GET the sandbox: if it closed as `box-lost`, start over; otherwise retry.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/sandboxes/{sandboxId}/screenshot": {
      "get": {
        "operationId": "getScreenshot",
        "tags": [
          "Sandboxes"
        ],
        "summary": "Take a screenshot",
        "description": "Returns a PNG of the slot's screen; only `gui` sandboxes have one. Screenshots are observations only: there is no click, typing or key-press API. To drive a UI, run a program on the Mac, or hand the sandbox a goal with `runAgent`, which a harness inside the slot carries out.",
        "security": [
          {
            "apiKey": []
          },
          {
            "consoleSession": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/SandboxId"
          },
          {
            "name": "maxDimension",
            "in": "query",
            "required": false,
            "description": "Scale the image so its longer side fits in this many pixels.",
            "schema": {
              "type": "integer",
              "minimum": 64,
              "maximum": 8192
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The screenshot.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "image/png": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "400": {
            "description": "`bad-max-dimension`: `maxDimension` must be an integer from 64 to 8192.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized`: no credential, an `Authorization` header that is not `Bearer \u2026`, a value that is not an `sk_offstage_` (or older `oc_live_`) key, a revoked or unknown key, or a box token. The message says which.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "`not-found`: no such sandbox, or it belongs to another team.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "`sandbox-not-ready`: the sandbox was still `pending` after waiting 30 s for a slot; the op never ran, so retry, or GET the sandbox until it is `ready`. `sandbox-closed`: the sandbox has closed; the message gives the reason. `op-failed`: the Mac reported an error; the message starts with the Mac's own code. `sandbox-busy` means 16 ops were running in the sandbox and 16 more waiting for a place, so this one was not taken: retry once some end. `headless-sandbox`: a `headless` sandbox has no screen; create one with `\"session\": \"gui\"`.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "504": {
            "description": "`op-timeout`: the Mac did not answer within the op's deadline (a command's `timeoutMs` plus 30 s; 90 s for a file or a screenshot), counted from when it took the op; or no place freed in the sandbox for the op within that deadline, and it never ran. GET the sandbox: if it closed as `box-lost`, start over; otherwise retry.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/sandboxes/{sandboxId}/agent": {
      "post": {
        "operationId": "runAgent",
        "tags": [
          "Sandboxes"
        ],
        "summary": "Hand the screen a goal",
        "description": "Hands a `gui` sandbox's screen a goal, which Claude carries out on the Mac itself: a harness inside the slot screenshots, asks the model what to do, and clicks and types there, until the model calls the goal done or failed, or `maxSteps` or `maxDurationMs` runs out. The model is called with the Anthropic API key in `x-anthropic-api-key`, billed to that key; the plane holds it in memory for this run only, and the Mac gets a token that dies with the run. No click or key crosses the network: goals, model text, steps and screenshots do. The answer comes within about 50 s: 200 if the run ended by then, else 202 with the run to follow with `getRun`. One run at a time per sandbox; the sandbox bills as usual while it runs.",
        "security": [
          {
            "apiKey": []
          },
          {
            "consoleSession": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/SandboxId"
          },
          {
            "name": "x-anthropic-api-key",
            "in": "header",
            "required": true,
            "description": "The Anthropic API key the run calls Claude with.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RunAgentRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The run ended within the hold.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AgentRun"
                }
              }
            }
          },
          "202": {
            "description": "The run is under way. Follow it with `GET /v1/runs/{runId}?wait=50`.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AgentRun"
                }
              }
            }
          },
          "400": {
            "description": "`not-an-object`, `unknown-field` (the fields are `goal`, `maxSteps`, `maxDurationMs`, `model`), `bad-goal`, `bad-max-steps`, `bad-max-duration-ms`, `bad-model`, or `missing-model-key` (no `x-anthropic-api-key` header).",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized`: no credential, an `Authorization` header that is not `Bearer \u2026`, a value that is not an `sk_offstage_` (or older `oc_live_`) key, a revoked or unknown key, or a box token. The message says which.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`forbidden`: a cookie-authenticated request that changes something came from another origin.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "`not-found`: no such sandbox, or it belongs to another team.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "`headless-sandbox`: a `headless` sandbox has no screen to drive; create one with `\"session\": \"gui\"`. `agent-running`: another run is driving this sandbox's screen; wait for it. `sandbox-not-ready`: the sandbox was still `pending` after waiting 30 s for a slot; nothing ran, so retry. `sandbox-closed`: the sandbox has closed.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "$ref": "#/components/responses/BodyTooLarge"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/runs/{runId}": {
      "get": {
        "operationId": "getRun",
        "tags": [
          "Sandboxes"
        ],
        "summary": "Follow a run",
        "description": "A run's state, verdict and every step so far. `?wait=N` (up to 60 s; the plane holds at most 50) holds the answer until the run ends or the time passes: 200 once it has ended, 202 while it runs. A finished run is kept for 24 hours.",
        "security": [
          {
            "apiKey": []
          },
          {
            "consoleSession": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/RunId"
          },
          {
            "name": "wait",
            "in": "query",
            "required": false,
            "description": "Seconds to hold the answer while the run is under way.",
            "schema": {
              "type": "number",
              "minimum": 0,
              "maximum": 60
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The run has ended.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AgentRun"
                }
              }
            }
          },
          "202": {
            "description": "The run is still under way.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AgentRun"
                }
              }
            }
          },
          "400": {
            "description": "`bad-wait`: `wait` must be a number of seconds from 0 to 60.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized`: no credential, an `Authorization` header that is not `Bearer \u2026`, a value that is not an `sk_offstage_` (or older `oc_live_`) key, a revoked or unknown key, or a box token. The message says which.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "`not-found`: no such run, it belongs to another team, or it ended more than 24 hours ago.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/runs/{runId}/frames/{frame}": {
      "get": {
        "operationId": "getRunFrame",
        "tags": [
          "Sandboxes"
        ],
        "summary": "Get a run's screenshot",
        "description": "A screenshot the run took, as a PNG: name it by a step's `frame`. Kept with the run's other artifacts.",
        "security": [
          {
            "apiKey": []
          },
          {
            "consoleSession": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/RunId"
          },
          {
            "name": "frame",
            "in": "path",
            "required": true,
            "description": "A step's `frame`, like `frame-0001.png`.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The screenshot.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "image/png": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized`: no credential, an `Authorization` header that is not `Bearer \u2026`, a value that is not an `sk_offstage_` (or older `oc_live_`) key, a revoked or unknown key, or a box token. The message says which.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "`not-found`: no such run or frame, or the run belongs to another team.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/capacity": {
      "get": {
        "operationId": "getCapacity",
        "tags": [
          "Capacity"
        ],
        "summary": "Fleet capacity",
        "description": "Counts the slots on online Macs that accept each kind of work, as their last claims said. A busy slot still counts, so this tells \"the fleet is busy and the work will queue\" from \"nothing takes this\", which is refused with 409 `no-capacity`; `gui` counts stay 0 until someone logs each slot in after its Mac boots.",
        "security": [
          {
            "apiKey": []
          },
          {
            "consoleSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "The counts.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Capacity"
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized`: no credential, an `Authorization` header that is not `Bearer \u2026`, a value that is not an `sk_offstage_` (or older `oc_live_`) key, a revoked or unknown key, or a box token. The message says which.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/health": {
      "get": {
        "operationId": "getHealth",
        "tags": [
          "Capacity"
        ],
        "summary": "Health",
        "description": "Says the plane is up, with the number of queued tasks and of sandboxes waiting for a slot. Public: no credential needed.",
        "security": [],
        "responses": {
          "200": {
            "description": "The plane is up.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Health"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/pricing": {
      "get": {
        "operationId": "getPricing",
        "tags": [
          "Billing"
        ],
        "summary": "The price sheet",
        "description": "The public price sheet: dollars per hour, minute and second of a sandbox or task, billed per second with no minimum, and the free credit. No credential needed.",
        "security": [],
        "responses": {
          "200": {
            "description": "The price sheet.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Pricing"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/usage": {
      "get": {
        "operationId": "getUsage",
        "tags": [
          "Billing"
        ],
        "summary": "Usage",
        "description": "The team's metered usage over the last `days` UTC days: totals, one row per day (zeros included), and one row per API key, most expensive first. Work counts on the day it ended, and work still running counts up to now; a `byKey` row with `keyId: null` is work started from the console.",
        "security": [
          {
            "apiKey": []
          },
          {
            "consoleSession": []
          }
        ],
        "parameters": [
          {
            "name": "days",
            "in": "query",
            "required": false,
            "description": "How many UTC days, ending today.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 90,
              "default": 30
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The usage.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Usage"
                }
              }
            }
          },
          "400": {
            "description": "`bad-days`: `days` must be an integer from 1 to 90.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized`: no credential, an `Authorization` header that is not `Bearer \u2026`, a value that is not an `sk_offstage_` (or older `oc_live_`) key, a revoked or unknown key, or a box token. The message says which.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/billing": {
      "get": {
        "operationId": "getBilling",
        "tags": [
          "Billing"
        ],
        "summary": "Billing state",
        "description": "The team's billing state: the card on file, the free credit, this month's spend and what in-flight work holds, the budget and its alerts, the credit ceiling a card is held to, and whether new work is paused (`status: past_due`).",
        "security": [
          {
            "apiKey": []
          },
          {
            "consoleSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "The billing object.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Billing"
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized`: no credential, an `Authorization` header that is not `Bearer \u2026`, a value that is not an `sk_offstage_` (or older `oc_live_`) key, a revoked or unknown key, or a box token. The message says which.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/billing/budget": {
      "put": {
        "operationId": "setBudget",
        "tags": [
          "Billing"
        ],
        "summary": "Set the budget",
        "description": "Sets the team's monthly budget (`limitUsd`, or null for none) and whether it is enforced, and returns the billing object; an enforced budget refuses work that could pass it. A new amount re-evaluates this month's alerts. Owners only. Console session only: an API key gets 403 `console-only`.",
        "x-console-only": true,
        "security": [
          {
            "consoleSession": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SetBudgetRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The billing object.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Billing"
                }
              }
            }
          },
          "400": {
            "description": "`not-an-object`, or `bad-budget` (`limitUsd` not a number from 0 to 1,000,000 or null, or `enforce` not a boolean).",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized`: no credential, an `Authorization` header that is not `Bearer \u2026`, a value that is not an `sk_offstage_` (or older `oc_live_`) key, a revoked or unknown key, or a box token. The message says which.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`console-only`: an API key may not do this; only a signed-in console session may. `forbidden`: the signed-in user is not an owner of the team, or the request came from another origin. `not-a-member`, `team-changed`: the session left the team while the request was in flight; nothing was changed.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/billing/checkout": {
      "post": {
        "operationId": "startCheckout",
        "tags": [
          "Billing"
        ],
        "summary": "Start adding a card",
        "description": "Starts adding a card and returns the checkout `url` to send the browser to: Stripe Checkout, or the plane's own stand-in in test mode. The browser comes back to `returnUrl` with `?checkout=success&session_id=\u2026` (then call `completeCheckout`) or `?checkout=cancelled`. Owners only. Console session only: an API key gets 403 `console-only`.",
        "x-console-only": true,
        "security": [
          {
            "consoleSession": []
          }
        ],
        "requestBody": {
          "required": true,
          "description": "A JSON object; `{}` returns to `/billing`.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ReturnUrlRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Where to send the browser.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Url"
                }
              }
            }
          },
          "400": {
            "description": "`not-an-object`, or `bad-return-url` (`returnUrl` is not a path or URL on this site).",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized`: no credential, an `Authorization` header that is not `Bearer \u2026`, a value that is not an `sk_offstage_` (or older `oc_live_`) key, a revoked or unknown key, or a box token. The message says which.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`console-only`: an API key may not do this; only a signed-in console session may. `forbidden`: the signed-in user is not an owner of the team, or the request came from another origin. `not-a-member`, `team-changed`: the session left the team while the request was in flight; nothing was changed.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/billing/checkout/complete": {
      "post": {
        "operationId": "completeCheckout",
        "tags": [
          "Billing"
        ],
        "summary": "Finish adding a card",
        "description": "Attaches the card from a finished checkout and returns the billing object with it. If the team is `past_due`, its failed invoices are charged again at once. Owners only. Console session only: an API key gets 403 `console-only`.",
        "x-console-only": true,
        "security": [
          {
            "consoleSession": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CompleteCheckoutRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The billing object, with the card.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Billing"
                }
              }
            }
          },
          "400": {
            "description": "`not-an-object`, or `missing-session` (`sessionId` is required).",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized`: no credential, an `Authorization` header that is not `Bearer \u2026`, a value that is not an `sk_offstage_` (or older `oc_live_`) key, a revoked or unknown key, or a box token. The message says which.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`console-only`: an API key may not do this; only a signed-in console session may. `forbidden`: the signed-in user is not an owner of the team, or the request came from another origin. `not-a-member`, `team-changed`: the session left the team while the request was in flight; nothing was changed.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "`not-found`: no such checkout session for this team.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "`checkout-incomplete`: the checkout has not attached a card yet; finish the checkout page first.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/billing/credit/checkout": {
      "post": {
        "operationId": "buyCredit",
        "tags": [
          "Billing"
        ],
        "summary": "Start buying credit",
        "description": "Starts buying `amountUsd` of credit, from $5 to $2,000, and returns the checkout `url` to send the browser to: Stripe Checkout in payment mode (which handles 3-D Secure and Apple Pay), or the plane's own stand-in in test mode. The card paid with is saved for auto-reload and becomes the card on file. The browser comes back to `returnUrl` with `?purchase=success&session_id=\u2026` (then call `completeCreditPurchase`) or `?purchase=cancelled`. The credit is granted once the payment succeeds, never twice, whether the console's return or Stripe's webhook lands first. Bought credit never expires. Owners only. Console session only: an API key gets 403 `console-only`.",
        "x-console-only": true,
        "security": [
          {
            "consoleSession": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BuyCreditRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Where to send the browser.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Url"
                }
              }
            }
          },
          "400": {
            "description": "`not-an-object`, `unknown-field`, `bad-amount` (`amountUsd` is not a whole number of cents from 5 to 2,000), or `bad-return-url`.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized`: no credential, an `Authorization` header that is not `Bearer \u2026`, a value that is not an `sk_offstage_` (or older `oc_live_`) key, a revoked or unknown key, or a box token. The message says which.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`console-only`: an API key may not do this; only a signed-in console session may. `forbidden`: the signed-in user is not an owner of the team, or the request came from another origin. `not-a-member`, `team-changed`: the session left the team while the request was in flight; nothing was changed.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "`billing-unavailable`: card payments are not open yet. `internal-team`: the operator's own team buys no credit.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/billing/credit/complete": {
      "post": {
        "operationId": "completeCreditPurchase",
        "tags": [
          "Billing"
        ],
        "summary": "Finish buying credit",
        "description": "Grants the credit a finished checkout paid for, if Stripe's webhook has not already, and returns the billing object with it. Calling it again, or after the webhook, grants nothing more. A team that was `postpaid` is on prepaid credit from then on. Owners only. Console session only: an API key gets 403 `console-only`.",
        "x-console-only": true,
        "security": [
          {
            "consoleSession": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CompleteCheckoutRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The billing object, with the credit.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Billing"
                }
              }
            }
          },
          "400": {
            "description": "`not-an-object`, or `missing-session` (`sessionId` is required).",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized`: no credential, an `Authorization` header that is not `Bearer \u2026`, a value that is not an `sk_offstage_` (or older `oc_live_`) key, a revoked or unknown key, or a box token. The message says which.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`console-only`: an API key may not do this; only a signed-in console session may. `forbidden`: the signed-in user is not an owner of the team, or the request came from another origin. `not-a-member`, `team-changed`: the session left the team while the request was in flight; nothing was changed.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "`not-found`: no such checkout session for this team.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "`checkout-incomplete`: the payment has not gone through yet. `not-a-purchase`: the session adds a card; finish it with `completeCheckout`.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/billing/auto-reload": {
      "put": {
        "operationId": "setAutoReload",
        "tags": [
          "Billing"
        ],
        "summary": "Turn auto-reload on or off",
        "description": "With `enabled: true`, whenever the balance (credit left, less what work in flight holds) falls below `thresholdUsd`, the card on file is charged off-session for what brings it back to `targetUsd`. `targetUsd` must be at least $5 above `thresholdUsd` and at most $2,000. At most 5 reloads a UTC day, one at a time, and an enforced monthly budget caps what reloads charge in a month. Turning it on while the balance is already under `thresholdUsd` charges the card at once. With no card on file the answer carries a checkout `url`: the page saves a card (and buys the first top-up when one is due), and auto-reload turns on when it completes. A reload the card refuses stops auto-reload (`status: failed`) and emails the owners; saving it again, or a new card, starts it again. A team that was `postpaid` is on prepaid credit from then on. Owners only. Console session only: an API key gets 403 `console-only`.",
        "x-console-only": true,
        "security": [
          {
            "consoleSession": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AutoReloadRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The billing object, and a checkout `url` when a card has to be saved first.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AutoReloadResult"
                }
              }
            }
          },
          "400": {
            "description": "`not-an-object`, `unknown-field`, `bad-auto-reload` (`enabled` not a boolean, an amount not in whole cents, `targetUsd` less than $5 above `thresholdUsd` or over $2,000), or `bad-return-url`.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized`: no credential, an `Authorization` header that is not `Bearer \u2026`, a value that is not an `sk_offstage_` (or older `oc_live_`) key, a revoked or unknown key, or a box token. The message says which.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`console-only`: an API key may not do this; only a signed-in console session may. `forbidden`: the signed-in user is not an owner of the team, or the request came from another origin. `not-a-member`, `team-changed`: the session left the team while the request was in flight; nothing was changed.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "`billing-unavailable`: card payments are not open yet. `internal-team`: the operator's own team buys no credit.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/billing/purchases": {
      "get": {
        "operationId": "listPurchases",
        "tags": [
          "Billing"
        ],
        "summary": "List credit bought",
        "description": "Every payment that became credit, newest first: when, how much, whether auto-reload charged it, and Stripe's receipt when Stripe gave one.",
        "security": [
          {
            "apiKey": []
          },
          {
            "consoleSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "The purchases.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PurchaseList"
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized`: no credential, an `Authorization` header that is not `Bearer \u2026`, a value that is not an `sk_offstage_` (or older `oc_live_`) key, a revoked or unknown key, or a box token. The message says which.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/billing/card": {
      "delete": {
        "operationId": "removeCard",
        "tags": [
          "Billing"
        ],
        "summary": "Remove the card",
        "description": "Removes the card on file and returns the billing object. Without a card, new work must fit in the remaining free credit. Owners only. Console session only: an API key gets 403 `console-only`.",
        "x-console-only": true,
        "security": [
          {
            "consoleSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "The billing object, without a card.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Billing"
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized`: no credential, an `Authorization` header that is not `Bearer \u2026`, a value that is not an `sk_offstage_` (or older `oc_live_`) key, a revoked or unknown key, or a box token. The message says which.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`console-only`: an API key may not do this; only a signed-in console session may. `forbidden`: the signed-in user is not an owner of the team, or the request came from another origin.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "`no-card`: there is no card on file.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/billing/portal": {
      "post": {
        "operationId": "openBillingPortal",
        "tags": [
          "Billing"
        ],
        "summary": "Open the customer portal",
        "description": "Returns a `url` to the payment provider's customer portal, which comes back to `returnUrl`. Test mode has no portal. Owners only. Console session only: an API key gets 403 `console-only`.",
        "x-console-only": true,
        "security": [
          {
            "consoleSession": []
          }
        ],
        "requestBody": {
          "required": true,
          "description": "A JSON object; `{}` returns to `/billing`.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ReturnUrlRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Where to send the browser.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Url"
                }
              }
            }
          },
          "400": {
            "description": "`not-an-object`, or `bad-return-url`.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized`: no credential, an `Authorization` header that is not `Bearer \u2026`, a value that is not an `sk_offstage_` (or older `oc_live_`) key, a revoked or unknown key, or a box token. The message says which.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`console-only`: an API key may not do this; only a signed-in console session may. `forbidden`: the signed-in user is not an owner of the team, or the request came from another origin. `not-a-member`, `team-changed`: the session left the team while the request was in flight; nothing was changed.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "`no-portal`: test mode has no customer portal. `no-customer`: add a card first.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/billing/invoices": {
      "get": {
        "operationId": "listInvoices",
        "tags": [
          "Billing"
        ],
        "summary": "List invoices",
        "description": "The team's monthly invoices, newest first, all at once (not paginated). Each UTC month that had usage gets one, charged to the card on file.",
        "security": [
          {
            "apiKey": []
          },
          {
            "consoleSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "The invoices.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "invoices": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Invoice"
                      }
                    }
                  },
                  "required": [
                    "invoices"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized`: no credential, an `Authorization` header that is not `Bearer \u2026`, a value that is not an `sk_offstage_` (or older `oc_live_`) key, a revoked or unknown key, or a box token. The message says which.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/auth/signup": {
      "post": {
        "operationId": "signup",
        "tags": [
          "Account"
        ],
        "summary": "Sign up",
        "description": "Creates a user and a team they own, signs them in by setting the `oc_session` cookie, and returns both. It emails a link that verifies the address; until the owner opens it, the team cannot create sandboxes, tasks or API keys (`team.verified` is false). Invites already sent to the email take effect too. No credential needed.",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SignupRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The new user and team.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              },
              "set-cookie": {
                "$ref": "#/components/headers/SetSession"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UserAndTeam"
                }
              }
            }
          },
          "400": {
            "description": "`not-an-object`, `bad-email`, `weak-password` (under 10 or over 256 characters), `bad-name`.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "`email-taken`: an account already exists for this email; sign in instead.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "`rate-limit`: this address has tried to sign up 5 times in the last hour. `signups-full`: the plane has taken all the signups it takes in a day. Both carry `Retry-After`.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              },
              "Retry-After": {
                "schema": {
                  "type": "string"
                },
                "description": "Seconds until a signup may go through."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/auth/login": {
      "post": {
        "operationId": "login",
        "tags": [
          "Account"
        ],
        "summary": "Sign in",
        "description": "Checks the email and password, sets the `oc_session` cookie for the user's own team (or their first team), and returns the user and that team. No credential needed.",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LoginRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The user and the team the session acts for.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              },
              "set-cookie": {
                "$ref": "#/components/headers/SetSession"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UserAndTeam"
                }
              }
            }
          },
          "400": {
            "description": "`not-an-object`, `bad-email`, `missing-password`.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "`bad-credentials`: the email and password do not match an account. `no-team`: the account belongs to no team.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "`too-many-attempts` (with `Retry-After`): 10 failed sign-ins for this email from this address in 15 minutes, or one once the email has taken 50 from all addresses in 15 minutes. That address may try again 15 minutes after its first miss; an address with no misses at the email is not paused. A password reset or a GitHub or Google sign-in lifts every pause on the email. `rate-limit`: this address has tried to sign in 20 times in the last minute.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/auth/config": {
      "get": {
        "operationId": "getAuthConfig",
        "tags": [
          "Account"
        ],
        "summary": "What the sign-in page offers",
        "description": "The Clerk publishable key the console's GitHub and Google buttons load Clerk with, and which of the two buttons to show, or null when this plane offers email sign-in alone; and the API address the console gives agents. Public: the key is meant for browsers. No credential needed.",
        "security": [],
        "responses": {
          "200": {
            "description": "The sign-in options.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthConfig"
                }
              }
            }
          }
        }
      }
    },
    "/v1/auth/clerk": {
      "post": {
        "operationId": "signInWithClerk",
        "tags": [
          "Account"
        ],
        "summary": "Sign in with GitHub or Google",
        "description": "Trades the token of the Clerk session the console's GitHub or Google sign-in just made for this plane's session cookie. The token is checked against the Clerk instance's keys, issuer, this site and its lifetime; the email comes from Clerk and must be one Clerk verified. Signs in the account at that address, or makes one (verified from the start, `201`, counted against the signup limits like a form signup). Signing in proves the address, so pending invites to it are joined. An unverified account at the address is reclaimed: its password stops working, its sessions end, and the personal API keys made as it on every team are revoked (`revokedKeys`). No credential needed.",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ClerkSignInRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Signed in to an existing account; the session cookie is set.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ClerkSignIn"
                }
              }
            }
          },
          "201": {
            "description": "A new account was made and signed in; the session cookie is set.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ClerkSignIn"
                }
              }
            }
          },
          "400": {
            "description": "`not-an-object`, `missing-token`.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "`bad-clerk-token`: the token is not a live Clerk session token for this plane and this site. `no-team`: the account belongs to no team.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`forbidden`: the request came from another origin. `email-unverified`: Clerk has no verified email for that GitHub or Google account. `account-locked`: Clerk has locked or banned the user. `signups-closed`: no account exists and the plane takes no signups.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "`social-sign-in-off`: this plane has no Clerk instance set up (CLERK_PUBLISHABLE_KEY).",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "`rate-limit`: 20 sign-ins a minute, or (for a new account) 5 signups an hour, from this client address. `signups-full`: the plane has taken its day's signups. Carries `Retry-After`.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "`clerk-unreachable`, `clerk-error`: Clerk did not answer.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/auth/logout": {
      "post": {
        "operationId": "logout",
        "tags": [
          "Account"
        ],
        "summary": "Sign out",
        "description": "Ends the console session, if the request carries one, and clears the cookie. It always answers 204.",
        "security": [
          {},
          {
            "consoleSession": []
          }
        ],
        "responses": {
          "204": {
            "description": "Signed out.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              },
              "set-cookie": {
                "description": "Clears `oc_session`.",
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/auth/verify": {
      "post": {
        "operationId": "verifyEmail",
        "tags": [
          "Account"
        ],
        "summary": "Verify an email",
        "description": "Spends the token from the link emailed at signup and marks its user's email verified, which lets their team run work and make keys. A token works once and lasts 24 hours; verifying spends every link the user holds. It does not sign in. No credential needed.",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/VerifyRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The user, now verified.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Verified"
                }
              }
            }
          },
          "400": {
            "description": "`not-an-object`. `bad-token`: the token is not a live one: it was used, it expired, or it was copied wrong; sign in and send a new link.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`forbidden`: the request came from another origin.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/auth/resend-verification": {
      "post": {
        "operationId": "resendVerification",
        "tags": [
          "Account"
        ],
        "summary": "Send the verification email again",
        "description": "Emails the signed-in user a new verification link. Earlier links keep working until they expire or one is used. At most 3 an hour per user.",
        "security": [
          {
            "consoleSession": []
          }
        ],
        "x-console-only": true,
        "responses": {
          "200": {
            "description": "Sent.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VerificationSent"
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized`: no console session.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`console-only`: an API key may not do this. `forbidden`: the request came from another origin.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "`already-verified`: this user's email is already verified.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "502": {
            "description": "`mail-failed`: the email could not be sent; try again in a few minutes.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/auth/forgot-password": {
      "post": {
        "operationId": "forgotPassword",
        "tags": [
          "Account"
        ],
        "summary": "Ask for a password-reset link",
        "description": "Mails a link to `/reset-password#token=\u2026` if the address has an account. The link works once and lasts 1 hour. The answer is the same whether or not an account exists, so it says nothing about which addresses have one. No credential needed.",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ForgotPasswordRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Asked. A link is on its way if the address has an account.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VerificationSent"
                }
              }
            }
          },
          "400": {
            "description": "`not-an-object`, `bad-email`.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`forbidden`: the request came from another origin.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "`rate-limit`: 3 reset emails were asked for this address in the last hour, or this client address asked 5 times in the last hour. Carries `Retry-After`.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/auth/reset-password": {
      "post": {
        "operationId": "resetPassword",
        "tags": [
          "Account"
        ],
        "summary": "Choose a new password",
        "description": "Spends the token from the reset link and sets the new password. Every other session the user had ends, every reset link they hold is spent, and their email counts as verified. Pending invites to the address are joined. Every personal API key the user made, on every team, is revoked; service keys are the team's and stay. The answer says how many sessions ended and keys were revoked. Sets the `oc_session` cookie for the user's own team (or their first team). No credential needed.",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ResetPasswordRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The user, the team the new session acts for, and what the reset invalidated.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              },
              "set-cookie": {
                "$ref": "#/components/headers/SetSession"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "user": {
                      "$ref": "#/components/schemas/User"
                    },
                    "team": {
                      "$ref": "#/components/schemas/Team"
                    },
                    "endedSessions": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "Console sessions the user had that the reset ended."
                    },
                    "revokedKeys": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "Personal API keys the user had made, on any team, that the reset revoked."
                    }
                  },
                  "required": [
                    "user",
                    "team",
                    "endedSessions",
                    "revokedKeys"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "400": {
            "description": "`not-an-object`. `bad-token`: the token is not a live one: it was used, it expired, or it was copied wrong; ask for a new link. `weak-password`: under 10 or over 256 characters; the link still works.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "`no-team`: the account belongs to no team.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`forbidden`: the request came from another origin.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/me": {
      "get": {
        "operationId": "getMe",
        "tags": [
          "Account"
        ],
        "summary": "The signed-in user",
        "description": "Returns the signed-in user, the team the session acts for, their role on it, and every team they belong to. Console session only: an API key gets 403 `console-only`.",
        "x-console-only": true,
        "security": [
          {
            "consoleSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "The session.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Me"
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized`: no credential, an `Authorization` header that is not `Bearer \u2026`, a value that is not an `sk_offstage_` (or older `oc_live_`) key, a revoked or unknown key, or a box token. The message says which.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`console-only`: an API key may not do this; only a signed-in console session may.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/me/team": {
      "post": {
        "operationId": "switchTeam",
        "tags": [
          "Account"
        ],
        "summary": "Switch teams",
        "description": "Points the session at another team the user belongs to; everything after acts for that team. An invited address is on the team only once it is verified. Console session only: an API key gets 403 `console-only`.",
        "x-console-only": true,
        "security": [
          {
            "consoleSession": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SwitchTeamRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The user, the team the session now acts for, and the role on it.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SessionTeam"
                }
              }
            }
          },
          "400": {
            "description": "`not-an-object`, or `missing-team` (`teamId` is required).",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized`: no credential, an `Authorization` header that is not `Bearer \u2026`, a value that is not an `sk_offstage_` (or older `oc_live_`) key, a revoked or unknown key, or a box token. The message says which.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`console-only`: an API key may not do this; only a signed-in console session may. `forbidden`: the request came from another origin.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "`not-found`: the user is not on that team.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/team": {
      "get": {
        "operationId": "getTeam",
        "tags": [
          "Account"
        ],
        "summary": "The team",
        "description": "Returns the caller's team and its limits.",
        "security": [
          {
            "apiKey": []
          },
          {
            "consoleSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "The team.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TeamWithLimits"
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized`: no credential, an `Authorization` header that is not `Bearer \u2026`, a value that is not an `sk_offstage_` (or older `oc_live_`) key, a revoked or unknown key, or a box token. The message says which.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "patch": {
        "operationId": "renameTeam",
        "tags": [
          "Account"
        ],
        "summary": "Rename the team",
        "description": "Renames the team and returns it. Owners only. Console session only: an API key gets 403 `console-only`.",
        "x-console-only": true,
        "security": [
          {
            "consoleSession": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RenameTeamRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The team.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Team"
                }
              }
            }
          },
          "400": {
            "description": "`not-an-object`, or `bad-name` (1 to 64 characters).",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized`: no credential, an `Authorization` header that is not `Bearer \u2026`, a value that is not an `sk_offstage_` (or older `oc_live_`) key, a revoked or unknown key, or a box token. The message says which.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`console-only`: an API key may not do this; only a signed-in console session may. `forbidden`: the signed-in user is not an owner of the team, or the request came from another origin. `not-a-member`, `team-changed`: the session left the team while the request was in flight; nothing was changed.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/team/members": {
      "get": {
        "operationId": "listMembers",
        "tags": [
          "Account"
        ],
        "summary": "List members",
        "description": "Lists the team's members, oldest first, and the invites not yet taken up. Console session only: an API key gets 403 `console-only`.",
        "x-console-only": true,
        "security": [
          {
            "consoleSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Members and invites.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Members"
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized`: no credential, an `Authorization` header that is not `Bearer \u2026`, a value that is not an `sk_offstage_` (or older `oc_live_`) key, a revoked or unknown key, or a box token. The message says which.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`console-only`: an API key may not do this; only a signed-in console session may.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "post": {
        "operationId": "inviteMember",
        "tags": [
          "Account"
        ],
        "summary": "Invite a member",
        "description": "Adds an existing user whose email is verified to the team at once as a `member` (answering `{member}`), or records an invite (answering `{invite}`) that takes effect when that address is proven: by opening its verification link, resetting its password, or signing in with GitHub or Google. Signing up alone joins nothing. Owners only, once the owner has verified their email. Console session only: an API key gets 403 `console-only`. Organizations only: a personal account gets 403 `personal-team`. The address is emailed. An invite lasts a week; inviting the address again renews it.",
        "x-console-only": true,
        "security": [
          {
            "consoleSession": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/InviteRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The new member, or the invite.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InviteResult"
                }
              }
            }
          },
          "400": {
            "description": "`not-an-object`, or `bad-email`.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized`: no credential, an `Authorization` header that is not `Bearer \u2026`, a value that is not an `sk_offstage_` (or older `oc_live_`) key, a revoked or unknown key, or a box token. The message says which.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`console-only`: an API key may not do this; only a signed-in console session may. `forbidden`: the signed-in user is not an owner of the team, or the request came from another origin. `email-unverified`: the owner has not verified their email yet. `not-a-member`, `team-changed`: the session left the team while the request was in flight; nothing was changed. `personal-team`: a personal account invites nobody; create an organization.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "`already-member`: that email is already on this team. `too-many-invites`: a team may have 50 invites out at once.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/team/members/{userId}": {
      "delete": {
        "operationId": "removeMember",
        "tags": [
          "Account"
        ],
        "summary": "Remove a member",
        "description": "Removes a member from the team, and revokes the personal API keys they made on it; service keys are the team's and stay. Anyone may remove themselves (leave); only an owner may remove someone else, and the last owner cannot be removed. Console session only: an API key gets 403 `console-only`.",
        "x-console-only": true,
        "security": [
          {
            "consoleSession": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/UserId"
          }
        ],
        "responses": {
          "204": {
            "description": "Removed.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "401": {
            "description": "`unauthorized`: no credential, an `Authorization` header that is not `Bearer \u2026`, a value that is not an `sk_offstage_` (or older `oc_live_`) key, a revoked or unknown key, or a box token. The message says which.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`console-only`: an API key may not do this. `forbidden`: only an owner may remove someone else, or the request came from another origin.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "`not-found`: that user is not on this team.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "`last-owner`: a team needs at least one owner.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "patch": {
        "operationId": "setMemberRole",
        "tags": [
          "Account"
        ],
        "summary": "Change a member's role",
        "description": "Makes a member an owner, or an owner a member, and returns the membership. Owners only; a team keeps at least one owner. Console session only: an API key gets 403 `console-only`.",
        "x-console-only": true,
        "security": [
          {
            "consoleSession": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/UserId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SetRoleRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The membership.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Member"
                }
              }
            }
          },
          "400": {
            "description": "`not-an-object`, or `bad-role` (`owner` or `member`).",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized`: no credential, an `Authorization` header that is not `Bearer \u2026`, a value that is not an `sk_offstage_` (or older `oc_live_`) key, a revoked or unknown key, or a box token. The message says which.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`console-only`: an API key may not do this. `forbidden`: only an owner may change roles, or the request came from another origin. `not-a-member`, `team-changed`: the session left the team while the request was in flight; nothing was changed.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "`not-found`: that user is not on this team.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "`last-owner`: a team needs at least one owner.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/team/invites/{email}": {
      "delete": {
        "operationId": "revokeInvite",
        "tags": [
          "Account"
        ],
        "summary": "Withdraw an invite",
        "description": "Withdraws a pending invite, so whoever later signs up with or proves that address does not join the team. Owners only. Console session only: an API key gets 403 `console-only`.",
        "x-console-only": true,
        "security": [
          {
            "consoleSession": []
          }
        ],
        "parameters": [
          {
            "name": "email",
            "in": "path",
            "required": true,
            "description": "The invited address, URL-encoded (`@` as `%40`). Matched without regard to case.",
            "schema": {
              "type": "string",
              "format": "email"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Withdrawn.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "401": {
            "description": "`unauthorized`: no credential, an `Authorization` header that is not `Bearer \u2026`, a value that is not an `sk_offstage_` (or older `oc_live_`) key, a revoked or unknown key, or a box token. The message says which.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`console-only`: an API key may not do this. `forbidden`: only an owner may withdraw invites, or the request came from another origin.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "`not-found`: there is no pending invite for that address on this team.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/keys": {
      "get": {
        "operationId": "listKeys",
        "tags": [
          "Account"
        ],
        "summary": "List API keys",
        "description": "Lists the team's live API keys, oldest first, by mask only: a key's secret is shown once, when it is created. Each says its `kind`: `personal` or `service`. Console session only: an API key gets 403 `console-only`.",
        "x-console-only": true,
        "security": [
          {
            "consoleSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "The keys.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "keys": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ApiKey"
                      }
                    }
                  },
                  "required": [
                    "keys"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized`: no credential, an `Authorization` header that is not `Bearer \u2026`, a value that is not an `sk_offstage_` (or older `oc_live_`) key, a revoked or unknown key, or a box token. The message says which.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`console-only`: an API key may not do this; only a signed-in console session may.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "post": {
        "operationId": "createKey",
        "tags": [
          "Account"
        ],
        "summary": "Create an API key",
        "description": "Creates an API key for the team and returns its secret in `key`, the only time it is ever shown; the plane keeps only its hash. Usage groups by the key's `name`. A person's key is `personal` (it goes when they leave the team or recover the account) unless an owner asks for `kind: \"service\"`, a key that belongs to the team and lasts until revoked. Needs the person's own email verified as well as the team's. Console session only: an API key gets 403 `console-only`.",
        "x-console-only": true,
        "security": [
          {
            "consoleSession": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateKeyRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The new key, with its secret.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreatedApiKey"
                }
              }
            }
          },
          "400": {
            "description": "`not-an-object`, `missing-name` (1 to 64 characters), or `bad-kind` (`personal` or `service`; the operator makes service keys only).",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized`: no credential, an `Authorization` header that is not `Bearer \u2026`, a value that is not an `sk_offstage_` (or older `oc_live_`) key, a revoked or unknown key, or a box token. The message says which.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`console-only`: an API key may not do this; only a signed-in console session may. `forbidden`: the request came from another origin, or a member asked for a service key. `email-unverified`: the caller or the team's owner has not verified their email yet; open the link emailed at signup, or send a new one. `not-a-member`, `team-changed`: the session left the team while the request was in flight; no key was made.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/keys/{keyId}": {
      "delete": {
        "operationId": "revokeKey",
        "tags": [
          "Account"
        ],
        "summary": "Revoke an API key",
        "description": "Revokes a key; it stops working at once. A revoked key cannot be restored, so make a new one. Console session only: an API key gets 403 `console-only`.",
        "x-console-only": true,
        "security": [
          {
            "consoleSession": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/KeyId"
          }
        ],
        "responses": {
          "204": {
            "description": "Revoked.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "401": {
            "description": "`unauthorized`: no credential, an `Authorization` header that is not `Bearer \u2026`, a value that is not an `sk_offstage_` (or older `oc_live_`) key, a revoked or unknown key, or a box token. The message says which.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`console-only`: an API key may not do this; only a signed-in console session may. `forbidden`: the request came from another origin.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "`not-found`: no such live key on this team.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/teams": {
      "post": {
        "operationId": "createOrganization",
        "tags": [
          "Account"
        ],
        "summary": "Create an organization",
        "description": "Creates an organization owned by the signed-in user and returns it. The session stays on its current team; switch with POST /v1/me/team. An organization has no free credit: it runs work once it has a card. Console session only: an API key gets 403 `console-only`.",
        "x-console-only": true,
        "security": [
          {
            "consoleSession": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateOrganizationRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The organization.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Team"
                }
              }
            }
          },
          "400": {
            "description": "`not-an-object`, `bad-name` (1 to 64 characters), or `bad-region`.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized`: no credential, an `Authorization` header that is not `Bearer \u2026`, a value that is not an `sk_offstage_` (or older `oc_live_`) key, a revoked or unknown key, or a box token. The message says which.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`console-only`: an API key may not do this. `email-unverified`: verify your email first. `forbidden`: the request came from another origin.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "`too-many-organizations`: one person may own 10.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/github": {
      "get": {
        "operationId": "getGitHub",
        "tags": [
          "GitHub"
        ],
        "summary": "The team's GitHub connection",
        "description": "Whether this plane runs GitHub Actions jobs at all (`configured`), the GitHub App installations linked to this team, and the last job that got no runner, with why. Any member may read it. Console session only: an API key gets 403 `console-only`.",
        "x-console-only": true,
        "security": [
          {
            "consoleSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "The connection.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GitHubConnection"
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized`: no credential, an `Authorization` header that is not `Bearer \u2026`, a value that is not an `sk_offstage_` (or older `oc_live_`) key, a revoked or unknown key, or a box token. The message says which.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`console-only`: an API key may not do this; only a signed-in console session may.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/github/install": {
      "post": {
        "operationId": "startGitHubInstall",
        "tags": [
          "GitHub"
        ],
        "summary": "Start connecting a GitHub organization",
        "description": "Returns the `url` of GitHub's sign-in page for the offstage app, and sets a cookie holding a signed state that names this team and the signed-in owner, good for 30 minutes. GitHub sends the browser back to `/v1/github/setup` on the console with a code; the plane learns from it who the person is on GitHub (their token is revoked at once, never kept) and links each organization installation of the app whose organization has them as an admin (an owner) and no offstage team has yet. With none, the browser goes on to GitHub's install page, and installing there brings it back the same way. The setup route checks GitHub's redirect against the cookie, so only the browser that started connecting can finish it, and redirects to `/ci?connected=<accounts>` or to `/ci?github_error=<code>`: `not-org-admin` (not an admin of the organization), `authorization-needed` (no code, declined, or expired), `installation-linked-elsewhere`, `state-expired`, `bad-state`, `wrong-team`, `signed-out`, `owner-only`, `install-requested` (a non-owner asked an organization owner to approve), `no-such-installation`, `github-error`, `github-unreachable`. Owners only, on a personal team or an organization. Console session only: an API key gets 403 `console-only`.",
        "x-console-only": true,
        "security": [
          {
            "consoleSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Where to send the browser.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Url"
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized`: no credential, an `Authorization` header that is not `Bearer \u2026`, a value that is not an `sk_offstage_` (or older `oc_live_`) key, a revoked or unknown key, or a box token. The message says which.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`console-only`: an API key may not do this; only a signed-in console session may. `forbidden`: the signed-in user is not an owner of the team, or the request came from another origin.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "description": "`github-not-configured`: this plane has no GitHub App, so GitHub Actions runners are off. `getGitHub` says `configured: false`.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/github/installations/{installationId}": {
      "delete": {
        "operationId": "disconnectGitHub",
        "tags": [
          "GitHub"
        ],
        "summary": "Disconnect a GitHub installation",
        "description": "Unlinks the installation from this team: none of its organization's jobs runs here any more, and the ones still waiting for a Mac are cancelled (their runner registrations deleted). The app stays installed on GitHub until an organization owner uninstalls it there; connecting again links it again. Owners only. Console session only: an API key gets 403 `console-only`.",
        "x-console-only": true,
        "security": [
          {
            "consoleSession": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/InstallationId"
          }
        ],
        "responses": {
          "204": {
            "description": "Disconnected.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "401": {
            "description": "`unauthorized`: no credential, an `Authorization` header that is not `Bearer \u2026`, a value that is not an `sk_offstage_` (or older `oc_live_`) key, a revoked or unknown key, or a box token. The message says which.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`console-only`: an API key may not do this; only a signed-in console session may. `forbidden`: the signed-in user is not an owner of the team, or the request came from another origin.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "`not-found`: no installation with this id is linked to this team.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "description": "`github-not-configured`: this plane has no GitHub App, so GitHub Actions runners are off. `getGitHub` says `configured: false`.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/recordings/{recordingId}": {
      "get": {
        "operationId": "getRecording",
        "tags": [
          "Recordings"
        ],
        "summary": "Get a recording",
        "description": "The timeline so far, or all of it once it has ended. To follow one live, use `GET /v1/recordings/{recordingId}/live`.",
        "security": [
          {
            "apiKey": []
          },
          {
            "consoleSession": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/RecordingId"
          }
        ],
        "responses": {
          "200": {
            "description": "The recording.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Recording"
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized`: no credential, an `Authorization` header that is not `Bearer \u2026`, a value that is not an `sk_offstage_` (or older `oc_live_`) key, a revoked or unknown key, or a box token. The message says which.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "`recording-not-found`: no recording by that id for this team. A sandbox made with `record: false`, a headless one, and another team's have none.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/recordings/{recordingId}/frames/{frame}": {
      "get": {
        "operationId": "getRecordingFrame",
        "tags": [
          "Recordings"
        ],
        "summary": "Get a recording's picture",
        "description": "A picture the timeline names, by an entry's `frame`: a JPEG the plane took, or a PNG an agent run's harness took. It never changes, so it may be cached.",
        "security": [
          {
            "apiKey": []
          },
          {
            "consoleSession": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/RecordingId"
          },
          {
            "name": "frame",
            "in": "path",
            "required": true,
            "description": "An entry's `frame`, like `frame-0001.jpg` or `run_\u2026-frame-0001.png`.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The picture.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "image/png": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "image/jpeg": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized`: no credential, an `Authorization` header that is not `Bearer \u2026`, a value that is not an `sk_offstage_` (or older `oc_live_`) key, a revoked or unknown key, or a box token. The message says which.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "`recording-not-found` for the recording, or `not-found` for a picture its timeline does not name.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/recordings/{recordingId}/live": {
      "get": {
        "operationId": "watchRecording",
        "tags": [
          "Recordings"
        ],
        "summary": "Follow a recording live",
        "description": "Server-sent events: an `entry` event (an id of its `seq`, and a RecordingEntry as data) for each entry after `after`, then each new one as it happens; then an `end` event (the Recording without its entries) and the stream closes. A comment every 15 s keeps it open, and a stream lasts at most 10 minutes: an EventSource reconnects by itself, and its `Last-Event-ID` header wins over `after`, so nothing comes twice.",
        "security": [
          {
            "apiKey": []
          },
          {
            "consoleSession": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/RecordingId"
          },
          {
            "name": "after",
            "in": "query",
            "required": false,
            "description": "The `seq` of the last entry you have. Default 0: everything.",
            "schema": {
              "type": "integer",
              "minimum": 0
            }
          },
          {
            "name": "Last-Event-ID",
            "in": "header",
            "required": false,
            "description": "Sent by an EventSource reconnecting: the last `seq` it saw.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The event stream.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "text/event-stream": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "description": "`bad-after`: `after` is not a whole number.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized`: no credential, an `Authorization` header that is not `Bearer \u2026`, a value that is not an `sk_offstage_` (or older `oc_live_`) key, a revoked or unknown key, or a box token. The message says which.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "`recording-not-found`: no recording by that id for this team. A sandbox made with `record: false`, a headless one, and another team's have none.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "apiKey": {
        "type": "http",
        "scheme": "bearer",
        "description": "A team API key, `sk_offstage_\u2026`, created in the console. Everything it touches belongs to its team."
      },
      "consoleSession": {
        "type": "apiKey",
        "in": "cookie",
        "name": "oc_session",
        "description": "The console's session cookie (httpOnly, SameSite=Lax, 30 days), set by `signup` and `login`."
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string",
                "description": "Stable and machine-readable, like `sandbox-closed`. Switch on this, not on the message."
              },
              "message": {
                "type": "string",
                "description": "What went wrong, for a person."
              },
              "fix": {
                "type": "string",
                "description": "What to do about it, when there is something to do."
              },
              "requestId": {
                "type": "string",
                "description": "The same as the `x-request-id` header. Quote it when asking for help.",
                "pattern": "^req_"
              }
            },
            "required": [
              "code",
              "message",
              "requestId"
            ],
            "additionalProperties": false
          }
        },
        "required": [
          "error"
        ],
        "additionalProperties": false,
        "description": "The one error envelope: every 4xx and 5xx the plane itself sends. A proxy in front of it may send a 5xx whose body is not JSON.",
        "examples": [
          {
            "error": {
              "code": "sandbox-closed",
              "message": "sandbox sbx_mh3k2f9aQ1xYz0aBcDeFgHiJ is closed (idle)",
              "fix": "create a new sandbox",
              "requestId": "req_mh3k2f9aQ1xYz0aBcDeFgHiJ"
            }
          }
        ]
      },
      "Millis": {
        "type": "integer",
        "minimum": 0,
        "description": "A time, in Unix milliseconds."
      },
      "Usd": {
        "type": "number",
        "description": "US dollars, exact to the micro-dollar (six decimal places)."
      },
      "Session": {
        "type": "string",
        "enum": [
          "headless",
          "gui"
        ],
        "description": "`headless`: the slot's user with no desktop; every slot takes it from boot. `gui`: the slot's logged-in desktop session, remade after every customer, so `gui` slots are scarce."
      },
      "CreatedBy": {
        "description": "Who started the work: `{keyName}` for an API key, `{email}` for a console user.",
        "oneOf": [
          {
            "type": "object",
            "properties": {
              "keyName": {
                "type": "string"
              }
            },
            "required": [
              "keyName"
            ],
            "additionalProperties": false
          },
          {
            "type": "object",
            "properties": {
              "email": {
                "type": "string"
              }
            },
            "required": [
              "email"
            ],
            "additionalProperties": false
          }
        ]
      },
      "TaskKind": {
        "type": "string",
        "enum": [
          "build",
          "test",
          "screenshot",
          "agent",
          "runner"
        ],
        "description": "Labels the task and picks its default `session`: `screenshot` runs `gui`, the rest `headless`. `agent` runs `agent` instead of `command`. `runner` is a GitHub Actions job: the plane makes one from a job queued with `runs-on: offstage-macos` (or `offstage-macos-gui`) in a connected GitHub organization, and its task carries `github`. No client submits one: `createTask` refuses it with 400 `bad-kind`."
      },
      "TaskState": {
        "type": "string",
        "enum": [
          "queued",
          "claimed",
          "running",
          "succeeded",
          "failed",
          "timed_out",
          "cancelled"
        ],
        "description": "`queued` \u2192 `claimed` \u2192 `running` \u2192 `succeeded` | `failed` | `timed_out`, or `cancelled` from any state before those. The last four are terminal."
      },
      "FailureReason": {
        "type": "string",
        "enum": [
          "exit",
          "setup",
          "timeout",
          "no-capacity",
          "box-lost",
          "plane-restart",
          "never-started"
        ],
        "description": "Why a task ended `failed` or `timed_out`; branch on this, never on event text. `exit`: the command exited non-zero (billed). `setup`: the Mac could not get to running it, such as a failed clone, or it exited 0 without a promised artifact (billed). `timeout`: it ran past `timeoutMs` (billed). `no-capacity`: no Mac took it within `queueTimeoutMs`; it never ran (free). `box-lost`, `plane-restart`, `never-started`: the fleet lost it; submit it again (free)."
      },
      "TaskSpec": {
        "type": "object",
        "properties": {
          "repo": {
            "type": "string",
            "description": "The https clone URL."
          },
          "kind": {
            "$ref": "#/components/schemas/TaskKind"
          },
          "command": {
            "type": "string",
            "description": "Present unless `kind` is `agent` or `runner`."
          },
          "agent": {
            "type": "string",
            "description": "Present only when `kind` is `agent`."
          },
          "artifacts": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Empty for a `runner` task: a GitHub job keeps its own artifacts on GitHub."
          },
          "session": {
            "$ref": "#/components/schemas/Session"
          },
          "timeoutMs": {
            "type": "integer",
            "minimum": 1
          },
          "queueTimeoutMs": {
            "type": "integer",
            "minimum": 1
          },
          "record": {
            "type": "boolean",
            "description": "`false` when it was submitted not to record."
          }
        },
        "required": [
          "repo",
          "kind",
          "artifacts",
          "session",
          "timeoutMs",
          "queueTimeoutMs"
        ],
        "additionalProperties": false,
        "description": "The task as submitted, with defaults filled in."
      },
      "TaskEvent": {
        "type": "object",
        "properties": {
          "at": {
            "$ref": "#/components/schemas/Millis"
          },
          "type": {
            "type": "string",
            "enum": [
              "claimed",
              "started",
              "log",
              "artifact",
              "succeeded",
              "failed",
              "timed_out",
              "cancelled"
            ]
          },
          "data": {
            "type": "string",
            "description": "`log`: one line of output (cut at 16,384 characters). `artifact`: its name. An outcome event: the reason, like `exit 1` or `cancelled by key ci`."
          }
        },
        "required": [
          "at",
          "type"
        ],
        "additionalProperties": false
      },
      "TaskSummary": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "`task_\u2026`.",
            "pattern": "^task_"
          },
          "spec": {
            "$ref": "#/components/schemas/TaskSpec"
          },
          "state": {
            "$ref": "#/components/schemas/TaskState"
          },
          "createdAt": {
            "$ref": "#/components/schemas/Millis"
          },
          "boxId": {
            "type": "string",
            "description": "The Mac that holds or ran it. Absent until a slot claims it.",
            "pattern": "^box_"
          },
          "slot": {
            "type": "integer",
            "description": "The slot on that Mac. Absent until claimed.",
            "minimum": 1
          },
          "startedAt": {
            "$ref": "#/components/schemas/Millis",
            "description": "When the command started. Absent until then."
          },
          "endedAt": {
            "$ref": "#/components/schemas/Millis",
            "description": "When it ended. Absent until then (and for a queued task that was cancelled)."
          },
          "attempts": {
            "type": "integer",
            "description": "How many slots claimed it and never started it.",
            "minimum": 0
          },
          "requeued": {
            "type": "boolean",
            "description": "True once a slot claimed it and never started it, so it went back to `queued`."
          },
          "runtimeSeconds": {
            "type": "integer",
            "description": "Whole seconds from start to end. Absent until it ends.",
            "minimum": 0
          },
          "costUsd": {
            "$ref": "#/components/schemas/Usd",
            "description": "What its running time costs: accrued so far while it runs, final once it ends. 0 for work the fleet lost."
          },
          "createdBy": {
            "$ref": "#/components/schemas/CreatedBy"
          },
          "exitCode": {
            "type": [
              "integer",
              "null"
            ],
            "description": "The command's exit status once the Mac reports it; null before then, and when there was none (timed out, cancelled, lost)."
          },
          "droppedLogLines": {
            "type": "integer",
            "description": "Log lines dropped from the front to keep the last 500.",
            "minimum": 0
          },
          "failureReason": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/FailureReason"
              },
              {
                "type": "null"
              }
            ],
            "description": "Why it ended `failed` or `timed_out`; null in every other state."
          },
          "github": {
            "$ref": "#/components/schemas/GitHubJob",
            "description": "Only on a `runner` task: the GitHub Actions job it was made for."
          },
          "replayUrl": {
            "type": "string",
            "description": "Where to watch it, live while it runs and as a replay after: the console's `/r/{id}` page. Present for a `gui` task that records."
          }
        },
        "required": [
          "id",
          "spec",
          "state",
          "createdAt",
          "attempts",
          "requeued",
          "costUsd",
          "createdBy",
          "exitCode",
          "droppedLogLines",
          "failureReason"
        ],
        "additionalProperties": false,
        "description": "A task in a list: the task without `events`. Fields that do not apply yet are absent, not null.",
        "examples": [
          {
            "id": "task_mh3k2f9aQ1xYz0aBcDeFgHiJ",
            "spec": {
              "repo": "https://github.com/org/app",
              "kind": "build",
              "command": "set -o pipefail; swift build 2>&1 | tee build.log",
              "artifacts": [
                "build.log"
              ],
              "session": "headless",
              "timeoutMs": 1800000,
              "queueTimeoutMs": 900000
            },
            "state": "running",
            "createdAt": 1793000000000,
            "boxId": "box_mh3k2f9aQ1xYz0aBcDeFgHiJ",
            "slot": 2,
            "startedAt": 1793000001000,
            "attempts": 0,
            "requeued": false,
            "costUsd": 0.02,
            "createdBy": {
              "keyName": "ci"
            },
            "exitCode": null,
            "droppedLogLines": 0,
            "failureReason": null
          }
        ]
      },
      "TaskView": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "`task_\u2026`.",
            "pattern": "^task_"
          },
          "spec": {
            "$ref": "#/components/schemas/TaskSpec"
          },
          "state": {
            "$ref": "#/components/schemas/TaskState"
          },
          "createdAt": {
            "$ref": "#/components/schemas/Millis"
          },
          "boxId": {
            "type": "string",
            "description": "The Mac that holds or ran it. Absent until a slot claims it.",
            "pattern": "^box_"
          },
          "slot": {
            "type": "integer",
            "description": "The slot on that Mac. Absent until claimed.",
            "minimum": 1
          },
          "startedAt": {
            "$ref": "#/components/schemas/Millis",
            "description": "When the command started. Absent until then."
          },
          "endedAt": {
            "$ref": "#/components/schemas/Millis",
            "description": "When it ended. Absent until then (and for a queued task that was cancelled)."
          },
          "attempts": {
            "type": "integer",
            "description": "How many slots claimed it and never started it.",
            "minimum": 0
          },
          "requeued": {
            "type": "boolean",
            "description": "True once a slot claimed it and never started it, so it went back to `queued`."
          },
          "runtimeSeconds": {
            "type": "integer",
            "description": "Whole seconds from start to end. Absent until it ends.",
            "minimum": 0
          },
          "costUsd": {
            "$ref": "#/components/schemas/Usd",
            "description": "What its running time costs: accrued so far while it runs, final once it ends. 0 for work the fleet lost."
          },
          "createdBy": {
            "$ref": "#/components/schemas/CreatedBy"
          },
          "exitCode": {
            "type": [
              "integer",
              "null"
            ],
            "description": "The command's exit status once the Mac reports it; null before then, and when there was none (timed out, cancelled, lost)."
          },
          "droppedLogLines": {
            "type": "integer",
            "description": "Log lines dropped from the front to keep the last 500.",
            "minimum": 0
          },
          "failureReason": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/FailureReason"
              },
              {
                "type": "null"
              }
            ],
            "description": "Why it ended `failed` or `timed_out`; null in every other state."
          },
          "events": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TaskEvent"
            },
            "description": "The whole story, in order. The log keeps its last 500 lines; when lines were dropped, the first `log` event says how many."
          },
          "github": {
            "$ref": "#/components/schemas/GitHubJob",
            "description": "Only on a `runner` task: the GitHub Actions job it was made for."
          },
          "replayUrl": {
            "type": "string",
            "description": "Where to watch it, live while it runs and as a replay after: the console's `/r/{id}` page. Present for a `gui` task that records."
          }
        },
        "required": [
          "id",
          "spec",
          "state",
          "createdAt",
          "attempts",
          "requeued",
          "costUsd",
          "createdBy",
          "exitCode",
          "droppedLogLines",
          "failureReason",
          "events"
        ],
        "additionalProperties": false,
        "description": "A task. Fields that do not apply yet (`boxId`, `slot`, `startedAt`, `endedAt`, `runtimeSeconds`) are absent, not null.",
        "examples": [
          {
            "id": "task_mh3k2f9aQ1xYz0aBcDeFgHiJ",
            "spec": {
              "repo": "https://github.com/org/app",
              "kind": "build",
              "command": "set -o pipefail; swift build 2>&1 | tee build.log",
              "artifacts": [
                "build.log"
              ],
              "session": "headless",
              "timeoutMs": 1800000,
              "queueTimeoutMs": 900000
            },
            "state": "running",
            "createdAt": 1793000000000,
            "boxId": "box_mh3k2f9aQ1xYz0aBcDeFgHiJ",
            "slot": 2,
            "startedAt": 1793000001000,
            "attempts": 0,
            "requeued": false,
            "events": [
              {
                "at": 1793000000500,
                "type": "claimed",
                "data": "box_mh3k2f9aQ1xYz0aBcDeFgHiJ slot 2"
              },
              {
                "at": 1793000001000,
                "type": "started"
              }
            ],
            "costUsd": 0.02,
            "createdBy": {
              "keyName": "ci"
            },
            "exitCode": null,
            "droppedLogLines": 0,
            "failureReason": null
          }
        ]
      },
      "TaskList": {
        "type": "object",
        "properties": {
          "tasks": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TaskSummary"
            }
          },
          "nextCursor": {
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Pass as `cursor` for the next page; null on the last page."
          }
        },
        "required": [
          "tasks",
          "nextCursor"
        ],
        "additionalProperties": false
      },
      "CreateTaskRequest": {
        "type": "object",
        "properties": {
          "repo": {
            "type": "string",
            "description": "The `https://` clone URL of a public repository, with no credentials in it. The URL is stored and shown on the task.",
            "minLength": 1,
            "maxLength": 2048
          },
          "kind": {
            "type": "string",
            "enum": [
              "build",
              "test",
              "screenshot",
              "agent"
            ],
            "description": "Labels the task and picks its default `session`: `screenshot` runs `gui`, the rest `headless`. `agent` runs `agent` instead of `command`. (`runner` tasks come from GitHub, never from this call.)"
          },
          "command": {
            "type": "string",
            "description": "The shell command line to run in the clone. Required for every kind except `agent`, which must not have one. Stored and shown on the task, so never put a secret in it. There is no `env`, `ref` or `branch` field: put them in the command.",
            "minLength": 1
          },
          "agent": {
            "type": "string",
            "description": "Only for `kind: agent`, and required there: the agent program the slot runs.",
            "minLength": 1
          },
          "artifacts": {
            "type": "array",
            "maxItems": 32,
            "uniqueItems": true,
            "default": [],
            "description": "File names in the clone's root that the task promises to upload; nothing else is kept. A name is 1 to 128 letters, digits, `.`, `_` and `-`, starts with a letter or digit, has no `..` and does not end in `.meta.json`. If the command exits 0, every promised artifact must exist and be non-empty. One artifact is at most 100 MiB, a task's together at most 256 MiB, and a team's stored artifacts at most 512 MiB; past any of those, or when the plane's artifact storage is full, the upload is refused with 413 `artifact-quota`, whose message names the share. Artifacts are deleted 7 days after their task ends.",
            "items": {
              "type": "string",
              "pattern": "^(?!.*\\.\\.)(?!.*\\.meta\\.json$)[a-zA-Z0-9][a-zA-Z0-9._-]{0,127}$"
            }
          },
          "session": {
            "$ref": "#/components/schemas/Session",
            "description": "Defaults by kind: `gui` for `screenshot`, `headless` otherwise."
          },
          "timeoutMs": {
            "type": "integer",
            "description": "Wall-clock cap on the whole task, clone included. 30 minutes by default.",
            "minimum": 1,
            "maximum": 86400000,
            "default": 1800000
          },
          "queueTimeoutMs": {
            "type": "integer",
            "description": "How long it may wait for a slot before failing with `failureReason: no-capacity`. 15 minutes by default. A team runs 4 tasks at once, and time spent behind the team's own earlier tasks while it has those 4 running does not count: the wait counts from the last moment the team held it back.",
            "minimum": 1,
            "maximum": 86400000,
            "default": 900000
          },
          "record": {
            "type": "boolean",
            "description": "Record a `gui` task's screen while it runs: pictures every few seconds and its log, playable after its slot is reset. Default true for `gui`."
          }
        },
        "required": [
          "repo",
          "kind"
        ],
        "additionalProperties": false,
        "description": "Unknown fields are refused with 400 `unknown-field`."
      },
      "Sandbox": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "`sbx_\u2026`.",
            "pattern": "^sbx_"
          },
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 48,
            "pattern": "^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$",
            "description": "What people call it, like `swift-otter`: picked at creation unless you choose one, and unique among the team's open sandboxes. Routes take the `id`, not the name."
          },
          "session": {
            "$ref": "#/components/schemas/Session"
          },
          "state": {
            "type": "string",
            "enum": [
              "pending",
              "ready",
              "closed"
            ],
            "description": "`pending` until a slot takes it, `ready` while it is held, then `closed`."
          },
          "createdAt": {
            "$ref": "#/components/schemas/Millis"
          },
          "expiresAt": {
            "$ref": "#/components/schemas/Millis",
            "description": "`createdAt + timeoutMs`: it closes then, whatever it is doing."
          },
          "timeoutMs": {
            "type": "integer",
            "description": "Its lifetime, counted from creation.",
            "minimum": 1
          },
          "idleTimeoutMs": {
            "type": "integer",
            "description": "It closes after this long with no op.",
            "minimum": 1
          },
          "lastActivityAt": {
            "$ref": "#/components/schemas/Millis"
          },
          "boxId": {
            "type": "string",
            "description": "The Mac holding it. Absent until a slot takes it.",
            "pattern": "^box_"
          },
          "slot": {
            "type": "integer",
            "description": "The slot on that Mac. Absent until taken.",
            "minimum": 1
          },
          "readyAt": {
            "$ref": "#/components/schemas/Millis",
            "description": "When it became `ready`; billing starts here. Absent until then."
          },
          "closedAt": {
            "$ref": "#/components/schemas/Millis",
            "description": "Absent until it closes."
          },
          "closeReason": {
            "$ref": "#/components/schemas/CloseReason",
            "description": "Absent until it closes."
          },
          "ops": {
            "type": "integer",
            "description": "Commands, file calls and screenshots run in it so far. The pictures the plane takes for its recording are not counted.",
            "minimum": 0
          },
          "runtimeSeconds": {
            "type": "integer",
            "description": "Whole seconds from `ready` to closed (or now). Absent until ready.",
            "minimum": 0
          },
          "costUsd": {
            "$ref": "#/components/schemas/Usd",
            "description": "What its time on a Mac costs: so far while open, final once closed."
          },
          "createdBy": {
            "$ref": "#/components/schemas/CreatedBy"
          },
          "record": {
            "type": "boolean",
            "description": "Whether the plane keeps a recording of it: true for a `gui` sandbox unless created with `record: false`."
          },
          "replayUrl": {
            "type": "string",
            "description": "Where to watch it, live while it is open and as a replay after: the console's `/r/{id}` page. Present when it records."
          }
        },
        "required": [
          "id",
          "name",
          "session",
          "state",
          "createdAt",
          "expiresAt",
          "timeoutMs",
          "idleTimeoutMs",
          "lastActivityAt",
          "ops",
          "costUsd",
          "createdBy"
        ],
        "additionalProperties": false,
        "description": "A sandbox: one Mac slot (a macOS user account) held for the team. Files persist between calls. Fields that do not apply yet are absent, not null.",
        "examples": [
          {
            "id": "sbx_mh3k2f9aQ1xYz0aBcDeFgHiJ",
            "name": "swift-otter",
            "session": "headless",
            "state": "ready",
            "createdAt": 1793000000000,
            "expiresAt": 1793001800000,
            "timeoutMs": 1800000,
            "idleTimeoutMs": 600000,
            "lastActivityAt": 1793000040000,
            "boxId": "box_mh3k2f9aQ1xYz0aBcDeFgHiJ",
            "slot": 3,
            "readyAt": 1793000000100,
            "ops": 12,
            "runtimeSeconds": 40,
            "costUsd": 0.002733,
            "createdBy": {
              "keyName": "ci"
            }
          }
        ]
      },
      "CloseReason": {
        "type": "string",
        "enum": [
          "deleted",
          "expired",
          "idle",
          "box-lost",
          "plane-restart",
          "no-capacity"
        ],
        "description": "`deleted`: killed. `expired`: reached `expiresAt`. `idle`: went `idleTimeoutMs` with no op. `box-lost`: its Mac stopped answering for 2 minutes. `plane-restart`: open when the plane restarted and its Mac did not come back with it (rare: a restart keeps open sandboxes `ready`). `no-capacity`: no slot took it within 15 minutes; it cost nothing."
      },
      "SandboxList": {
        "type": "object",
        "properties": {
          "sandboxes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Sandbox"
            }
          },
          "nextCursor": {
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Pass as `cursor` for the next page; null on the last page."
          }
        },
        "required": [
          "sandboxes",
          "nextCursor"
        ],
        "additionalProperties": false
      },
      "CreateSandboxRequest": {
        "type": "object",
        "properties": {
          "session": {
            "$ref": "#/components/schemas/Session",
            "default": "headless"
          },
          "timeoutMs": {
            "type": "integer",
            "description": "How long it may live, from creation. 1 hour by default, 24 hours at most.",
            "minimum": 1,
            "maximum": 86400000,
            "default": 3600000
          },
          "idleTimeoutMs": {
            "type": "integer",
            "description": "It closes after this long with no op. 10 minutes by default (or `timeoutMs`, if shorter), and never more than `timeoutMs`.",
            "minimum": 1,
            "maximum": 86400000,
            "default": 600000
          },
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 48,
            "pattern": "^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$",
            "description": "1 to 48 lowercase letters, digits and inner hyphens. Left out, the plane picks an adjective-noun name. 409 `name-taken` if an open sandbox of the team already has it."
          },
          "record": {
            "type": "boolean",
            "description": "Keep a recording of a `gui` sandbox's screen (its timeline: pictures, commands, agent steps). Default true for `gui`; a `headless` sandbox has no screen to record."
          }
        },
        "additionalProperties": false,
        "description": "Unknown fields are refused with 400 `unknown-field`."
      },
      "SetTimeoutRequest": {
        "type": "object",
        "properties": {
          "timeoutMs": {
            "type": "integer",
            "description": "Close the sandbox this long from now. At most 24 hours from its creation.",
            "minimum": 1,
            "maximum": 86400000
          }
        },
        "required": [
          "timeoutMs"
        ],
        "additionalProperties": false,
        "description": "Unknown fields are refused with 400 `unknown-field`."
      },
      "RunCommandRequest": {
        "type": "object",
        "properties": {
          "cmd": {
            "type": "string",
            "description": "A shell command line, run as `zsh -lc \"<cmd>\"`. Each command is a new process, so `cd` and `export` do not carry over; use `cwd` and `envs`. There is no stdin and no streaming output.",
            "minLength": 1,
            "maxLength": 65536
          },
          "cwd": {
            "type": "string",
            "description": "The working directory, relative to the sandbox root (created if missing). The root by default. Same rules as a file `path`.",
            "minLength": 1,
            "maxLength": 1024,
            "pattern": "^(?!/)(?!(.*/)?\\.\\.(/|$))[A-Za-z0-9._/-]+$"
          },
          "envs": {
            "type": "object",
            "description": "Extra environment variables for this command only.",
            "maxProperties": 64,
            "propertyNames": {
              "pattern": "^[A-Za-z_][A-Za-z0-9_]*$"
            },
            "additionalProperties": {
              "type": "string"
            }
          },
          "timeoutMs": {
            "type": "integer",
            "description": "10 minutes by default. Never past the sandbox's own end: the plane cuts it to what is left of the sandbox's life when the Mac takes the command, so lengthening the sandbox while the command waits for a place lengthens it too. A command already running keeps the end it started with. At the timeout the command's whole process tree is killed and the result has `timedOut: true`.",
            "minimum": 1,
            "maximum": 1800000,
            "default": 600000
          }
        },
        "required": [
          "cmd"
        ],
        "additionalProperties": false,
        "description": "Unknown fields are refused with 400 `unknown-field`."
      },
      "CommandResult": {
        "type": "object",
        "description": "A command that ended. The fields after `state` are the Mac's own answer, relayed as it sent them.",
        "properties": {
          "commandId": {
            "type": "string",
            "description": "`cmd_\u2026`.",
            "pattern": "^cmd_"
          },
          "state": {
            "const": "finished"
          },
          "exitCode": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Null when the command was killed at its timeout."
          },
          "timedOut": {
            "type": "boolean"
          },
          "stdout": {
            "type": "string",
            "description": "The last 1 MiB of standard output. In a `gui` sandbox it carries both streams."
          },
          "stderr": {
            "type": "string",
            "description": "The last 1 MiB of standard error. Empty in a `gui` sandbox."
          },
          "truncated": {
            "type": "boolean",
            "description": "True when the start of a stream was dropped to keep its last 1 MiB."
          },
          "durationMs": {
            "type": "integer",
            "minimum": 0
          }
        },
        "required": [
          "commandId",
          "state",
          "exitCode",
          "timedOut",
          "stdout",
          "stderr",
          "truncated",
          "durationMs"
        ],
        "examples": [
          {
            "commandId": "cmd_mh3k2f9aQ1xYz0aBcDeFgHiJ",
            "state": "finished",
            "exitCode": 0,
            "timedOut": false,
            "stdout": "Build complete!\n",
            "stderr": "",
            "truncated": false,
            "durationMs": 48213
          }
        ]
      },
      "CommandRunning": {
        "type": "object",
        "properties": {
          "commandId": {
            "type": "string",
            "description": "Follow it at `GET /v1/sandboxes/{sandboxId}/commands/{commandId}`.",
            "pattern": "^cmd_"
          },
          "state": {
            "const": "running"
          },
          "startedAt": {
            "$ref": "#/components/schemas/Millis"
          }
        },
        "required": [
          "commandId",
          "state",
          "startedAt"
        ],
        "additionalProperties": false,
        "description": "A command still running.",
        "examples": [
          {
            "commandId": "cmd_mh3k2f9aQ1xYz0aBcDeFgHiJ",
            "state": "running",
            "startedAt": 1793000000000
          }
        ]
      },
      "FileWritten": {
        "type": "object",
        "properties": {
          "path": {
            "type": "string",
            "description": "The path as the plane normalized it (`./a//b` becomes `a/b`)."
          },
          "bytes": {
            "type": "integer",
            "description": "How many bytes were written.",
            "minimum": 0
          }
        },
        "required": [
          "path",
          "bytes"
        ],
        "additionalProperties": false
      },
      "SessionCounts": {
        "type": "object",
        "properties": {
          "headless": {
            "type": "integer",
            "minimum": 0
          },
          "gui": {
            "type": "integer",
            "minimum": 0
          }
        },
        "required": [
          "headless",
          "gui"
        ],
        "additionalProperties": false
      },
      "Capacity": {
        "type": "object",
        "properties": {
          "tasks": {
            "$ref": "#/components/schemas/SessionCounts",
            "description": "Slots that take tasks, per session."
          },
          "sandboxes": {
            "$ref": "#/components/schemas/SessionCounts",
            "description": "Slots that take sandboxes, per session."
          },
          "boxesOnline": {
            "type": "integer",
            "description": "Macs heard from in the last 45 s.",
            "minimum": 0
          }
        },
        "required": [
          "tasks",
          "sandboxes",
          "boxesOnline"
        ],
        "additionalProperties": false,
        "examples": [
          {
            "tasks": {
              "headless": 6,
              "gui": 0
            },
            "sandboxes": {
              "headless": 6,
              "gui": 0
            },
            "boxesOnline": 1
          }
        ]
      },
      "Health": {
        "type": "object",
        "properties": {
          "ok": {
            "const": true
          },
          "queueDepth": {
            "type": "integer",
            "description": "Queued tasks.",
            "minimum": 0
          },
          "sandboxesPending": {
            "type": "integer",
            "description": "Sandboxes waiting for a slot.",
            "minimum": 0
          },
          "boxesOnline": {
            "type": "integer",
            "description": "Boxes online right now (a heartbeat within the last 45 s).",
            "minimum": 0
          }
        },
        "required": [
          "ok",
          "queueDepth",
          "sandboxesPending",
          "boxesOnline"
        ],
        "additionalProperties": false
      },
      "Pricing": {
        "type": "object",
        "properties": {
          "currency": {
            "const": "usd"
          },
          "usdPerHour": {
            "$ref": "#/components/schemas/Usd",
            "description": "The price of an hour of a sandbox or task."
          },
          "usdPerMinute": {
            "$ref": "#/components/schemas/Usd",
            "description": "The same price per minute."
          },
          "usdPerSecond": {
            "$ref": "#/components/schemas/Usd"
          },
          "billing": {
            "type": "string",
            "description": "`per second, no minimum`. Each piece of work rounds up to the whole second."
          },
          "freeCreditUsd": {
            "$ref": "#/components/schemas/Usd",
            "description": "What a new team may spend before it adds a card."
          },
          "concurrentSandboxes": {
            "type": "integer",
            "description": "Open sandboxes a team may hold at once.",
            "minimum": 1
          }
        },
        "required": [
          "currency",
          "usdPerHour",
          "usdPerMinute",
          "usdPerSecond",
          "billing",
          "freeCreditUsd",
          "concurrentSandboxes"
        ],
        "additionalProperties": false,
        "examples": [
          {
            "currency": "usd",
            "usdPerHour": 1.1,
            "usdPerMinute": 0.018333,
            "usdPerSecond": 0.00030556,
            "billing": "per second, no minimum",
            "freeCreditUsd": 5,
            "concurrentSandboxes": 4
          }
        ]
      },
      "Usage": {
        "type": "object",
        "properties": {
          "period": {
            "type": "string",
            "description": "This month, `YYYY-MM` (UTC)."
          },
          "from": {
            "$ref": "#/components/schemas/Millis",
            "description": "The first millisecond of the window."
          },
          "to": {
            "$ref": "#/components/schemas/Millis",
            "description": "Now."
          },
          "totals": {
            "type": "object",
            "properties": {
              "seconds": {
                "type": "integer",
                "minimum": 0
              },
              "costUsd": {
                "$ref": "#/components/schemas/Usd"
              },
              "tasks": {
                "type": "integer",
                "minimum": 0
              },
              "sandboxes": {
                "type": "integer",
                "minimum": 0
              }
            },
            "required": [
              "seconds",
              "costUsd",
              "tasks",
              "sandboxes"
            ],
            "additionalProperties": false
          },
          "daily": {
            "type": "array",
            "description": "One row per UTC day in the window, oldest first, zeros included.",
            "items": {
              "type": "object",
              "properties": {
                "date": {
                  "type": "string",
                  "description": "`YYYY-MM-DD`."
                },
                "seconds": {
                  "type": "integer",
                  "minimum": 0
                },
                "costUsd": {
                  "$ref": "#/components/schemas/Usd"
                },
                "tasks": {
                  "type": "integer",
                  "minimum": 0
                },
                "sandboxes": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "required": [
                "date",
                "seconds",
                "costUsd",
                "tasks",
                "sandboxes"
              ],
              "additionalProperties": false
            }
          },
          "byKey": {
            "type": "array",
            "description": "One row per API key that ran work in the window, most expensive first.",
            "items": {
              "type": "object",
              "properties": {
                "keyId": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "description": "Null for work started from the console."
                },
                "name": {
                  "type": "string",
                  "description": "The key's name, `<name> (revoked)` for a revoked key, or `Console`."
                },
                "seconds": {
                  "type": "integer",
                  "minimum": 0
                },
                "costUsd": {
                  "$ref": "#/components/schemas/Usd"
                }
              },
              "required": [
                "keyId",
                "name",
                "seconds",
                "costUsd"
              ],
              "additionalProperties": false
            }
          }
        },
        "required": [
          "period",
          "from",
          "to",
          "totals",
          "daily",
          "byKey"
        ],
        "additionalProperties": false
      },
      "Card": {
        "type": "object",
        "properties": {
          "brand": {
            "type": "string"
          },
          "last4": {
            "type": "string"
          },
          "expMonth": {
            "type": "integer",
            "minimum": 1,
            "maximum": 12
          },
          "expYear": {
            "type": "integer"
          }
        },
        "required": [
          "brand",
          "last4",
          "expMonth",
          "expYear"
        ],
        "additionalProperties": false
      },
      "Billing": {
        "type": "object",
        "properties": {
          "mode": {
            "type": "string",
            "enum": [
              "test",
              "live",
              "off"
            ],
            "description": "`test`: the built-in test checkout or a Stripe test key. `live`: a live Stripe key. `off`: card payments are not open yet, so every team runs on its free credit."
          },
          "plan": {
            "type": "object",
            "properties": {
              "id": {
                "const": "payg"
              },
              "name": {
                "type": "string"
              },
              "usdPerHour": {
                "$ref": "#/components/schemas/Usd"
              },
              "concurrentSandboxes": {
                "type": "integer",
                "minimum": 1
              }
            },
            "required": [
              "id",
              "name",
              "usdPerHour",
              "concurrentSandboxes"
            ],
            "additionalProperties": false
          },
          "card": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/Card"
              },
              {
                "type": "null"
              }
            ],
            "description": "Null until a card is added."
          },
          "credit": {
            "type": "object",
            "properties": {
              "grantedUsd": {
                "$ref": "#/components/schemas/Usd"
              },
              "usedUsd": {
                "$ref": "#/components/schemas/Usd"
              },
              "remainingUsd": {
                "$ref": "#/components/schemas/Usd"
              },
              "expiresAt": {
                "oneOf": [
                  {
                    "$ref": "#/components/schemas/Millis"
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "When the soonest active grant with credit left expires; null when none of them does."
              },
              "grants": {
                "type": "array",
                "description": "Every grant, newest first, expired and revoked ones included (`active` false, nothing remaining).",
                "items": {
                  "$ref": "#/components/schemas/CreditGrant"
                }
              }
            },
            "required": [
              "grantedUsd",
              "usedUsd",
              "remainingUsd",
              "expiresAt",
              "grants"
            ],
            "additionalProperties": false,
            "description": "Credit is what a team spends before its card is charged. It comes as grants: the sign-up credit, and any offstage gives (the YC deal), each with its own expiry or none. The sums count the grants still active. Work draws on the grants that were active when it was admitted, the soonest to expire first."
          },
          "period": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "description": "This month, `YYYY-MM` (UTC)."
              },
              "start": {
                "$ref": "#/components/schemas/Millis"
              },
              "end": {
                "$ref": "#/components/schemas/Millis"
              },
              "spentUsd": {
                "$ref": "#/components/schemas/Usd",
                "description": "Booked this month, plus what running work has used so far."
              },
              "reservedUsd": {
                "$ref": "#/components/schemas/Usd",
                "description": "What in-flight work holds beyond what it has already used."
              },
              "estimatedInvoiceUsd": {
                "$ref": "#/components/schemas/Usd",
                "description": "This month's spend minus the credit it uses, plus anything carried over."
              }
            },
            "required": [
              "id",
              "start",
              "end",
              "spentUsd",
              "reservedUsd",
              "estimatedInvoiceUsd"
            ],
            "additionalProperties": false
          },
          "budget": {
            "type": "object",
            "properties": {
              "limitUsd": {
                "type": [
                  "number",
                  "null"
                ],
                "description": "The monthly budget; null for none."
              },
              "enforce": {
                "type": "boolean",
                "description": "When true, work that could take this month's spend beyond the credit past the budget is refused with 402 `budget-exceeded`."
              },
              "spentUsd": {
                "$ref": "#/components/schemas/Usd",
                "description": "What counts against the budget this month: spend beyond the credit, booked and accrued. Credit never counts against a budget."
              },
              "alertThresholds": {
                "type": "array",
                "items": {
                  "type": "integer"
                },
                "description": "Percentages of the budget at which an alert fires, once a month each."
              },
              "alerts": {
                "type": "array",
                "description": "This month's alerts.",
                "items": {
                  "type": "object",
                  "properties": {
                    "threshold": {
                      "type": "integer"
                    },
                    "at": {
                      "$ref": "#/components/schemas/Millis"
                    },
                    "spentUsd": {
                      "$ref": "#/components/schemas/Usd"
                    }
                  },
                  "required": [
                    "threshold",
                    "at",
                    "spentUsd"
                  ],
                  "additionalProperties": false
                }
              }
            },
            "required": [
              "limitUsd",
              "enforce",
              "spentUsd",
              "alertThresholds",
              "alerts"
            ],
            "additionalProperties": false
          },
          "ceiling": {
            "type": "object",
            "description": "The credit ceiling: the most a card on file is trusted with at once beyond the free credit, set by offstage ($100 unless offstage set this team's own). While a card is on file, work that could pass it is refused with 402 `credit-ceiling`, whatever the budget says; the budget can only refuse sooner. Without a card the free credit is the limit.",
            "properties": {
              "limitUsd": {
                "$ref": "#/components/schemas/Usd"
              },
              "usedUsd": {
                "$ref": "#/components/schemas/Usd",
                "description": "What counts against it now, beyond the free credit: invoices not paid yet and amounts carried, usage not invoiced yet (this month's included), and what in-flight work could still cost at its timeout."
              },
              "remainingUsd": {
                "$ref": "#/components/schemas/Usd"
              }
            },
            "required": [
              "limitUsd",
              "usedUsd",
              "remainingUsd"
            ],
            "additionalProperties": false
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "past_due"
            ],
            "description": "`past_due`: the last charge failed and new work is refused with 402 `payment-failed` until the card is fixed."
          },
          "failureMessage": {
            "type": "string",
            "description": "Present when `past_due`: why the charge failed."
          },
          "portal": {
            "type": "boolean",
            "description": "Whether `openBillingPortal` works (false in test mode)."
          },
          "postpaid": {
            "type": "boolean",
            "description": "Legacy month-end invoicing: usage beyond the credit is invoiced on the 1st, under the credit ceiling. False for every team made since prepaid credit, and for good once a postpaid team buys credit or turns auto-reload on."
          },
          "autoReload": {
            "$ref": "#/components/schemas/AutoReload"
          }
        },
        "required": [
          "mode",
          "plan",
          "card",
          "credit",
          "period",
          "budget",
          "ceiling",
          "postpaid",
          "autoReload",
          "status",
          "portal"
        ],
        "additionalProperties": false
      },
      "AutoReload": {
        "type": "object",
        "description": "Auto-reload: when the balance (credit left, less what work in flight holds) falls below `thresholdUsd`, the card on file is charged for what brings it back to `targetUsd`.",
        "properties": {
          "enabled": {
            "type": "boolean"
          },
          "status": {
            "type": "string",
            "enum": [
              "off",
              "on",
              "reloading",
              "failed"
            ],
            "description": "`reloading`: a charge is in flight. `failed`: the last reload's charge did not go through; nothing is charged until an owner saves auto-reload again or changes the card."
          },
          "thresholdUsd": {
            "$ref": "#/components/schemas/Usd"
          },
          "targetUsd": {
            "$ref": "#/components/schemas/Usd"
          },
          "reloadsToday": {
            "type": "integer",
            "minimum": 0,
            "description": "Reloads started today (UTC)."
          },
          "maxReloadsPerDay": {
            "type": "integer",
            "minimum": 1
          },
          "reloadedThisMonthUsd": {
            "$ref": "#/components/schemas/Usd",
            "description": "What auto-reload charged this month (UTC), and what it is charging now."
          },
          "failureMessage": {
            "type": "string",
            "description": "Present when `failed`: why the card did not pay."
          },
          "failedAt": {
            "$ref": "#/components/schemas/Millis"
          },
          "skipped": {
            "type": "object",
            "description": "Present when a reload was due and not made: the day's 5 were used (`daily-cap`), it would pass the enforced monthly budget (`budget`), or there is no card (`no-card`).",
            "properties": {
              "reason": {
                "type": "string",
                "enum": [
                  "daily-cap",
                  "budget",
                  "no-card"
                ]
              },
              "message": {
                "type": "string"
              },
              "at": {
                "$ref": "#/components/schemas/Millis"
              }
            },
            "required": [
              "reason",
              "message",
              "at"
            ],
            "additionalProperties": false
          }
        },
        "required": [
          "enabled",
          "status",
          "thresholdUsd",
          "targetUsd",
          "reloadsToday",
          "maxReloadsPerDay",
          "reloadedThisMonthUsd"
        ],
        "additionalProperties": false
      },
      "Purchase": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "`pur_\u2026`"
          },
          "at": {
            "$ref": "#/components/schemas/Millis"
          },
          "amountUsd": {
            "$ref": "#/components/schemas/Usd"
          },
          "auto": {
            "type": "boolean",
            "description": "Charged by auto-reload, not bought on the checkout page."
          },
          "receiptUrl": {
            "type": [
              "string",
              "null"
            ],
            "description": "Stripe's receipt, when Stripe gave one."
          }
        },
        "required": [
          "id",
          "at",
          "amountUsd",
          "auto",
          "receiptUrl"
        ],
        "additionalProperties": false
      },
      "PurchaseList": {
        "type": "object",
        "properties": {
          "purchases": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Purchase"
            }
          }
        },
        "required": [
          "purchases"
        ],
        "additionalProperties": false
      },
      "BuyCreditRequest": {
        "type": "object",
        "properties": {
          "amountUsd": {
            "type": "number",
            "minimum": 5,
            "maximum": 2000,
            "description": "Dollars, in whole cents."
          },
          "returnUrl": {
            "type": "string",
            "description": "A path or URL on this site to come back to. `/billing` by default."
          }
        },
        "required": [
          "amountUsd"
        ],
        "additionalProperties": false
      },
      "AutoReloadRequest": {
        "type": "object",
        "properties": {
          "enabled": {
            "type": "boolean"
          },
          "thresholdUsd": {
            "type": "number",
            "minimum": 0,
            "description": "Reload when the balance falls below this. Left out: the current one ($5 to begin with)."
          },
          "targetUsd": {
            "type": "number",
            "maximum": 2000,
            "description": "Bring the balance back up to this: at least $5 above `thresholdUsd`. Left out: the current one ($15 to begin with)."
          },
          "returnUrl": {
            "type": "string",
            "description": "Where a checkout that saves a card comes back to. `/billing` by default."
          }
        },
        "required": [
          "enabled"
        ],
        "additionalProperties": false
      },
      "AutoReloadResult": {
        "type": "object",
        "properties": {
          "url": {
            "type": [
              "string",
              "null"
            ],
            "description": "A checkout page that saves a card first, when there is none; null when auto-reload is saved."
          },
          "billing": {
            "$ref": "#/components/schemas/Billing"
          }
        },
        "required": [
          "url",
          "billing"
        ],
        "additionalProperties": false
      },
      "Invoice": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "`inv_\u2026`.",
            "pattern": "^inv_"
          },
          "number": {
            "type": "string",
            "description": "`OC-YYYYMM-NNNN`."
          },
          "period": {
            "type": "string",
            "description": "The month it bills, `YYYY-MM` (UTC)."
          },
          "createdAt": {
            "$ref": "#/components/schemas/Millis"
          },
          "subtotalUsd": {
            "$ref": "#/components/schemas/Usd"
          },
          "creditUsd": {
            "$ref": "#/components/schemas/Usd",
            "description": "The free credit this usage consumed."
          },
          "carriedUsd": {
            "$ref": "#/components/schemas/Usd",
            "description": "Carried from earlier months that were under the $0.50 minimum charge."
          },
          "totalUsd": {
            "$ref": "#/components/schemas/Usd",
            "description": "What was charged, in whole cents."
          },
          "status": {
            "type": "string",
            "enum": [
              "paid",
              "open",
              "failed",
              "deferred"
            ],
            "description": "`open`: owed and not paid yet, a charge still under way included. `deferred`: under the $0.50 minimum charge, so carried to the next month."
          },
          "hostedUrl": {
            "type": [
              "string",
              "null"
            ],
            "description": "The payment provider's page for it, when there is one."
          },
          "lines": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "description": {
                  "type": "string"
                },
                "amountUsd": {
                  "$ref": "#/components/schemas/Usd"
                }
              },
              "required": [
                "description",
                "amountUsd"
              ],
              "additionalProperties": false
            }
          },
          "failureMessage": {
            "type": "string",
            "description": "Present when the charge failed."
          }
        },
        "required": [
          "id",
          "number",
          "period",
          "createdAt",
          "subtotalUsd",
          "creditUsd",
          "carriedUsd",
          "totalUsd",
          "status",
          "hostedUrl",
          "lines"
        ],
        "additionalProperties": false
      },
      "SetBudgetRequest": {
        "type": "object",
        "properties": {
          "limitUsd": {
            "type": [
              "number",
              "null"
            ],
            "minimum": 0,
            "maximum": 1000000,
            "description": "Dollars per month, or null for no budget."
          },
          "enforce": {
            "type": "boolean"
          }
        },
        "required": [
          "limitUsd",
          "enforce"
        ],
        "additionalProperties": true
      },
      "ReturnUrlRequest": {
        "type": "object",
        "properties": {
          "returnUrl": {
            "type": "string",
            "description": "A path or URL on this site to come back to. `/billing` by default."
          }
        },
        "additionalProperties": true
      },
      "CompleteCheckoutRequest": {
        "type": "object",
        "properties": {
          "sessionId": {
            "type": "string",
            "description": "The `session_id` the browser came back with.",
            "minLength": 1
          }
        },
        "required": [
          "sessionId"
        ],
        "additionalProperties": true
      },
      "Url": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string",
            "description": "Send the browser here. In test mode it is a path on this plane."
          }
        },
        "required": [
          "url"
        ],
        "additionalProperties": false
      },
      "Role": {
        "type": "string",
        "enum": [
          "owner",
          "member"
        ]
      },
      "User": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "`usr_\u2026`.",
            "pattern": "^usr_"
          },
          "email": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "createdAt": {
            "$ref": "#/components/schemas/Millis"
          },
          "emailVerified": {
            "type": "boolean",
            "description": "Whether they opened the verification link emailed at signup."
          }
        },
        "required": [
          "id",
          "email",
          "name",
          "createdAt",
          "emailVerified"
        ],
        "additionalProperties": false
      },
      "Team": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "`team_\u2026`.",
            "pattern": "^team_"
          },
          "name": {
            "type": "string"
          },
          "slug": {
            "type": "string"
          },
          "createdAt": {
            "$ref": "#/components/schemas/Millis"
          },
          "verified": {
            "type": "boolean",
            "description": "Whether the team may run work and make keys: its owner verified their email. False only for a new team until then."
          },
          "kind": {
            "type": "string",
            "enum": [
              "personal",
              "organization"
            ],
            "description": "`personal`: the team signing up made, which holds one person and invites nobody. `organization`: made with POST /v1/teams to share."
          },
          "region": {
            "type": "string",
            "enum": [
              "us"
            ],
            "description": "Where the team's Macs run. `us` only for now."
          }
        },
        "required": [
          "id",
          "name",
          "slug",
          "createdAt",
          "verified",
          "kind",
          "region"
        ],
        "additionalProperties": false
      },
      "TeamWithLimits": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "`team_\u2026`.",
            "pattern": "^team_"
          },
          "name": {
            "type": "string"
          },
          "slug": {
            "type": "string"
          },
          "createdAt": {
            "$ref": "#/components/schemas/Millis"
          },
          "verified": {
            "type": "boolean",
            "description": "Whether the team may run work and make keys: its owner verified their email. False only for a new team until then."
          },
          "limits": {
            "type": "object",
            "properties": {
              "concurrentSandboxes": {
                "type": "integer",
                "description": "Open sandboxes the team may hold at once.",
                "minimum": 1
              },
              "maxSandboxTimeoutMs": {
                "type": "integer",
                "minimum": 1
              },
              "maxTaskTimeoutMs": {
                "type": "integer",
                "minimum": 1
              }
            },
            "required": [
              "concurrentSandboxes",
              "maxSandboxTimeoutMs",
              "maxTaskTimeoutMs"
            ],
            "additionalProperties": false
          },
          "kind": {
            "type": "string",
            "enum": [
              "personal",
              "organization"
            ],
            "description": "`personal`: the team signing up made, which holds one person and invites nobody. `organization`: made with POST /v1/teams to share."
          },
          "region": {
            "type": "string",
            "enum": [
              "us"
            ],
            "description": "Where the team's Macs run. `us` only for now."
          }
        },
        "required": [
          "id",
          "name",
          "slug",
          "createdAt",
          "verified",
          "limits",
          "kind",
          "region"
        ],
        "additionalProperties": false
      },
      "UserAndTeam": {
        "type": "object",
        "properties": {
          "user": {
            "$ref": "#/components/schemas/User"
          },
          "team": {
            "$ref": "#/components/schemas/Team"
          }
        },
        "required": [
          "user",
          "team"
        ],
        "additionalProperties": false
      },
      "SessionTeam": {
        "type": "object",
        "properties": {
          "user": {
            "$ref": "#/components/schemas/User"
          },
          "team": {
            "$ref": "#/components/schemas/Team"
          },
          "role": {
            "$ref": "#/components/schemas/Role"
          }
        },
        "required": [
          "user",
          "team",
          "role"
        ],
        "additionalProperties": false
      },
      "Me": {
        "type": "object",
        "properties": {
          "user": {
            "$ref": "#/components/schemas/User"
          },
          "team": {
            "$ref": "#/components/schemas/Team",
            "description": "The team the session acts for."
          },
          "role": {
            "$ref": "#/components/schemas/Role"
          },
          "teams": {
            "type": "array",
            "description": "Every team the user belongs to.",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string"
                },
                "name": {
                  "type": "string"
                },
                "role": {
                  "$ref": "#/components/schemas/Role"
                },
                "kind": {
                  "type": "string",
                  "enum": [
                    "personal",
                    "organization"
                  ]
                }
              },
              "required": [
                "id",
                "name",
                "role",
                "kind"
              ],
              "additionalProperties": false
            }
          }
        },
        "required": [
          "user",
          "team",
          "role",
          "teams"
        ],
        "additionalProperties": false
      },
      "Member": {
        "type": "object",
        "properties": {
          "userId": {
            "type": "string"
          },
          "email": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "role": {
            "$ref": "#/components/schemas/Role"
          },
          "addedAt": {
            "$ref": "#/components/schemas/Millis"
          }
        },
        "required": [
          "userId",
          "email",
          "name",
          "role",
          "addedAt"
        ],
        "additionalProperties": false
      },
      "PendingInvite": {
        "type": "object",
        "properties": {
          "email": {
            "type": "string"
          },
          "invitedAt": {
            "$ref": "#/components/schemas/Millis"
          },
          "invitedBy": {
            "type": [
              "string",
              "null"
            ],
            "description": "The inviter's email, or null if they no longer have an account."
          },
          "expiresAt": {
            "$ref": "#/components/schemas/Millis",
            "description": "A week after it was sent or last renewed. An expired invite is not honoured; invite the address again to renew it."
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "expired"
            ]
          }
        },
        "required": [
          "email",
          "invitedAt",
          "invitedBy",
          "expiresAt",
          "status"
        ],
        "additionalProperties": false
      },
      "Members": {
        "type": "object",
        "properties": {
          "members": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Member"
            }
          },
          "invites": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PendingInvite"
            }
          }
        },
        "required": [
          "members",
          "invites"
        ],
        "additionalProperties": false
      },
      "InviteResult": {
        "description": "`{member}` when the email already had a verified account, which is now on the team; `{invite}` otherwise. `emailed` says whether a mail went out: at most one per address and organization every 10 minutes, however often the invite is renewed.",
        "oneOf": [
          {
            "type": "object",
            "properties": {
              "member": {
                "$ref": "#/components/schemas/Member"
              },
              "emailed": {
                "type": "boolean",
                "description": "A mail went out with this call (false when one was sent in the last 10 minutes, or sending failed)."
              }
            },
            "required": [
              "member",
              "emailed"
            ],
            "additionalProperties": false
          },
          {
            "type": "object",
            "properties": {
              "invite": {
                "$ref": "#/components/schemas/PendingInvite"
              },
              "emailed": {
                "type": "boolean",
                "description": "A mail went out with this call (false when one was sent in the last 10 minutes, or sending failed)."
              }
            },
            "required": [
              "invite",
              "emailed"
            ],
            "additionalProperties": false
          }
        ]
      },
      "ApiKey": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "`key_\u2026`. Not a credential.",
            "pattern": "^key_"
          },
          "name": {
            "type": "string"
          },
          "mask": {
            "type": "string",
            "description": "Like `sk_offstage_AbCd\u2026WxYz`. Not usable as a credential."
          },
          "createdAt": {
            "$ref": "#/components/schemas/Millis"
          },
          "lastUsedAt": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Unix milliseconds, or null if never used."
          },
          "createdBy": {
            "oneOf": [
              {
                "type": "object",
                "properties": {
                  "email": {
                    "type": "string"
                  }
                },
                "required": [
                  "email"
                ],
                "additionalProperties": false
              },
              {
                "type": "null"
              }
            ],
            "description": "The console user who made it, or null."
          },
          "kind": {
            "type": "string",
            "enum": [
              "personal",
              "service"
            ],
            "description": "`personal`: made by a person; revoked when they leave or are removed from the team, or recover the account (password reset, or a GitHub/Google sign-in reclaiming an unverified account). `service`: the team's (an owner's on purpose, or the operator's); revoked only explicitly."
          }
        },
        "required": [
          "id",
          "name",
          "mask",
          "createdAt",
          "lastUsedAt",
          "createdBy",
          "kind"
        ],
        "additionalProperties": false
      },
      "CreatedApiKey": {
        "type": "object",
        "properties": {
          "key": {
            "type": "string",
            "description": "The secret, `sk_offstage_\u2026`. Shown this once only; send it as `Authorization: Bearer <key>`.",
            "pattern": "^sk_offstage_"
          },
          "id": {
            "type": "string",
            "description": "`key_\u2026`.",
            "pattern": "^key_"
          },
          "keyId": {
            "type": "string",
            "description": "The same as `id`.",
            "pattern": "^key_"
          },
          "name": {
            "type": "string"
          },
          "mask": {
            "type": "string"
          },
          "createdAt": {
            "$ref": "#/components/schemas/Millis"
          },
          "lastUsedAt": {
            "type": [
              "integer",
              "null"
            ]
          },
          "createdBy": {
            "oneOf": [
              {
                "type": "object",
                "properties": {
                  "email": {
                    "type": "string"
                  }
                },
                "required": [
                  "email"
                ],
                "additionalProperties": false
              },
              {
                "type": "null"
              }
            ]
          },
          "kind": {
            "type": "string",
            "enum": [
              "personal",
              "service"
            ],
            "description": "`personal`: made by a person; revoked when they leave or are removed from the team, or recover the account (password reset, or a GitHub/Google sign-in reclaiming an unverified account). `service`: the team's (an owner's on purpose, or the operator's); revoked only explicitly."
          }
        },
        "required": [
          "key",
          "id",
          "keyId",
          "name",
          "mask",
          "createdAt",
          "lastUsedAt",
          "createdBy",
          "kind"
        ],
        "additionalProperties": false
      },
      "SignupRequest": {
        "type": "object",
        "properties": {
          "email": {
            "type": "string",
            "maxLength": 254
          },
          "password": {
            "type": "string",
            "minLength": 10,
            "maxLength": 256
          },
          "name": {
            "type": "string",
            "description": "Your name; the part of the email before `@` by default.",
            "maxLength": 64
          }
        },
        "required": [
          "email",
          "password"
        ],
        "additionalProperties": true
      },
      "LoginRequest": {
        "type": "object",
        "properties": {
          "email": {
            "type": "string"
          },
          "password": {
            "type": "string"
          }
        },
        "required": [
          "email",
          "password"
        ],
        "additionalProperties": true
      },
      "SwitchTeamRequest": {
        "type": "object",
        "properties": {
          "teamId": {
            "type": "string",
            "description": "One of the user's teams, from `getMe`."
          }
        },
        "required": [
          "teamId"
        ],
        "additionalProperties": true
      },
      "RenameTeamRequest": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 64
          }
        },
        "required": [
          "name"
        ],
        "additionalProperties": true
      },
      "InviteRequest": {
        "type": "object",
        "properties": {
          "email": {
            "type": "string"
          }
        },
        "required": [
          "email"
        ],
        "additionalProperties": true
      },
      "CreateKeyRequest": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "What the key is for, like `ci`; usage groups by it.",
            "minLength": 1,
            "maxLength": 64
          },
          "kind": {
            "type": "string",
            "enum": [
              "personal",
              "service"
            ],
            "description": "Defaults to `personal` for a person. Only an owner may make a `service` key; the operator's keys are always `service`."
          }
        },
        "required": [
          "name"
        ],
        "additionalProperties": true
      },
      "VerifyRequest": {
        "type": "object",
        "properties": {
          "token": {
            "type": "string",
            "description": "The token after `#token=` in the emailed link."
          }
        },
        "required": [
          "token"
        ],
        "additionalProperties": true
      },
      "ForgotPasswordRequest": {
        "type": "object",
        "properties": {
          "email": {
            "type": "string"
          }
        },
        "required": [
          "email"
        ],
        "additionalProperties": true
      },
      "ResetPasswordRequest": {
        "type": "object",
        "properties": {
          "token": {
            "type": "string",
            "description": "The token after `#token=` in the emailed reset link."
          },
          "password": {
            "type": "string",
            "minLength": 10,
            "maxLength": 256
          }
        },
        "required": [
          "token",
          "password"
        ],
        "additionalProperties": true
      },
      "Verified": {
        "type": "object",
        "properties": {
          "user": {
            "$ref": "#/components/schemas/User"
          }
        },
        "required": [
          "user"
        ],
        "additionalProperties": false
      },
      "VerificationSent": {
        "type": "object",
        "properties": {
          "sentTo": {
            "type": "string",
            "description": "The address the new link went to."
          }
        },
        "required": [
          "sentTo"
        ],
        "additionalProperties": false
      },
      "AuthConfig": {
        "type": "object",
        "properties": {
          "clerkPublishableKey": {
            "type": [
              "string",
              "null"
            ],
            "description": "pk_live_\u2026 or pk_test_\u2026, or null when GitHub and Google sign-in are off."
          },
          "socialProviders": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "github",
                "google"
              ]
            },
            "description": "The buttons the sign-in page shows, in this order. Empty when clerkPublishableKey is null."
          },
          "apiOrigin": {
            "type": "string",
            "description": "The API's address, which the console gives agents: API_ORIGIN when the request reached the console's own hostname (CONSOLE_ORIGIN), else the origin it reached."
          }
        },
        "required": [
          "clerkPublishableKey",
          "socialProviders",
          "apiOrigin"
        ],
        "additionalProperties": false
      },
      "ClerkSignInRequest": {
        "type": "object",
        "properties": {
          "token": {
            "type": "string",
            "description": "The Clerk session token (a JWT) from Clerk's session.getToken()."
          }
        },
        "required": [
          "token"
        ],
        "additionalProperties": true
      },
      "ClerkSignIn": {
        "type": "object",
        "properties": {
          "user": {
            "$ref": "#/components/schemas/User"
          },
          "team": {
            "$ref": "#/components/schemas/Team"
          },
          "created": {
            "type": "boolean",
            "description": "True when this sign-in made the account."
          },
          "revokedKeys": {
            "type": "integer",
            "minimum": 0,
            "description": "Personal API keys revoked because this sign-in reclaimed an unverified account at the address; 0 otherwise."
          }
        },
        "required": [
          "user",
          "team",
          "created",
          "revokedKeys"
        ],
        "additionalProperties": false
      },
      "CreateOrganizationRequest": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "1 to 64 characters."
          },
          "region": {
            "type": "string",
            "enum": [
              "us"
            ],
            "description": "Defaults to `us`, the only region for now."
          }
        },
        "required": [
          "name"
        ]
      },
      "SetRoleRequest": {
        "type": "object",
        "properties": {
          "role": {
            "$ref": "#/components/schemas/Role"
          }
        },
        "required": [
          "role"
        ]
      },
      "RenameSandboxRequest": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 48,
            "pattern": "^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$",
            "description": "The new name. Closed sandboxes can be renamed too; only open ones must be unique."
          }
        },
        "required": [
          "name"
        ],
        "additionalProperties": false,
        "description": "Unknown fields are refused with 400 `unknown-field`."
      },
      "GitHubJob": {
        "type": "object",
        "description": "The GitHub Actions job a `runner` task runs. GitHub gives a waiting job to any idle runner of its organization whose labels cover it, so once `jobStartedAt` is set these fields describe the job this task's runner actually took, which need not be the one it was registered for. A runner task whose runner got no job is cancelled once no job it could take still waits, and costs nothing.",
        "properties": {
          "org": {
            "type": "string",
            "description": "The GitHub organization the runner was registered on."
          },
          "repo": {
            "type": "string",
            "description": "`owner/name` of the job's repository."
          },
          "workflowJobId": {
            "type": "integer",
            "description": "GitHub's job id."
          },
          "runId": {
            "type": "integer",
            "description": "GitHub's workflow run id."
          },
          "runUrl": {
            "type": "string",
            "description": "The job's page on GitHub."
          },
          "jobName": {
            "type": "string"
          },
          "workflowName": {
            "type": "string"
          },
          "jobStartedAt": {
            "type": "integer",
            "description": "When GitHub said this task's runner took the job (Unix ms). The task is billed, per second from its Mac's start, only once this is set; until then its `costUsd` is 0."
          },
          "runnerName": {
            "type": "string",
            "description": "The runner GitHub says picked the job up. Absent until it did."
          },
          "conclusion": {
            "type": "string",
            "description": "GitHub's verdict once the job completed: `success`, `failure`, `cancelled`, `skipped`, `timed_out`, and so on. Absent until then."
          }
        },
        "required": [
          "org",
          "repo",
          "workflowJobId",
          "runId",
          "runUrl",
          "jobName",
          "workflowName"
        ],
        "additionalProperties": false
      },
      "GitHubInstallation": {
        "type": "object",
        "description": "An installation of the offstage GitHub App, linked to this team: its organization's jobs that ask for `offstage-macos` run here, on this team's bill.",
        "properties": {
          "installationId": {
            "type": "integer"
          },
          "account": {
            "type": "string",
            "description": "The organization's (or user's) login on GitHub."
          },
          "accountType": {
            "type": "string",
            "description": "`Organization` or `User`."
          },
          "supported": {
            "type": "boolean",
            "description": "False for a personal account's installation: only an organization can have the runners offstage registers, so none of its jobs runs here."
          },
          "reason": {
            "type": "string",
            "description": "Why it is not supported. Absent when it is."
          },
          "linkedAt": {
            "$ref": "#/components/schemas/Millis"
          }
        },
        "required": [
          "installationId",
          "account",
          "accountType",
          "supported",
          "linkedAt"
        ],
        "additionalProperties": false
      },
      "GitHubRefusal": {
        "type": "object",
        "description": "The last job one of this team's organizations queued for an offstage Mac that got no runner, and why. GitHub keeps such a job waiting; re-run it once the cause is fixed.",
        "properties": {
          "code": {
            "type": "string",
            "description": "The error code the job met: `payment-method-required`, `credit-ceiling`, `budget-exceeded`, `payment-failed`, `email-unverified`, `no-capacity`, `task-backlog`, `queue-full`, `github-error`, `github-unreachable`."
          },
          "message": {
            "type": "string"
          },
          "at": {
            "$ref": "#/components/schemas/Millis"
          },
          "repo": {
            "type": "string",
            "description": "`owner/name`."
          },
          "jobName": {
            "type": "string"
          },
          "runUrl": {
            "type": "string",
            "description": "The job's page on GitHub."
          }
        },
        "required": [
          "code",
          "message",
          "at",
          "repo",
          "jobName",
          "runUrl"
        ],
        "additionalProperties": false
      },
      "GitHubConnection": {
        "type": "object",
        "properties": {
          "configured": {
            "type": "boolean",
            "description": "False when this plane has no GitHub App: GitHub Actions runners are off, and the console shows none of this."
          },
          "appSlug": {
            "type": [
              "string",
              "null"
            ],
            "description": "The app's name in its URL, github.com/apps/<appSlug>. Null when not configured."
          },
          "installations": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/GitHubInstallation"
            },
            "description": "Oldest first."
          },
          "lastRefusal": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/GitHubRefusal"
              },
              {
                "type": "null"
              }
            ],
            "description": "The last refused job, if it was in the last 24 hours and no job has run since."
          }
        },
        "required": [
          "configured",
          "appSlug",
          "installations",
          "lastRefusal"
        ],
        "additionalProperties": false,
        "examples": [
          {
            "configured": true,
            "appSlug": "offstage",
            "installations": [
              {
                "installationId": 51234567,
                "account": "acme",
                "accountType": "Organization",
                "supported": true,
                "linkedAt": 1793000000000
              }
            ],
            "lastRefusal": null
          }
        ]
      },
      "AgentModel": {
        "type": "string",
        "enum": [
          "claude-opus-5-5",
          "claude-sonnet-5-5",
          "claude-haiku-5-5",
          "claude-fable-5-1",
          "claude-opus-5"
        ],
        "description": "The Claude model a run calls: one with the computer toolset."
      },
      "RunAgentRequest": {
        "type": "object",
        "properties": {
          "goal": {
            "type": "string",
            "minLength": 1,
            "maxLength": 4000,
            "description": "What should be true when the run is done, like \"TextEdit has a document saved as ~/Desktop/note.rtf that says hello\". At most 4,000 characters."
          },
          "maxSteps": {
            "type": "integer",
            "minimum": 1,
            "maximum": 100,
            "default": 30,
            "description": "Model turns the run may take; one turn may carry several actions."
          },
          "maxDurationMs": {
            "type": "integer",
            "minimum": 10000,
            "maximum": 1800000,
            "default": 600000,
            "description": "How long the run may take."
          },
          "model": {
            "$ref": "#/components/schemas/AgentModel"
          }
        },
        "required": [
          "goal"
        ],
        "additionalProperties": false,
        "examples": [
          {
            "goal": "TextEdit has a document saved as ~/Desktop/note.txt that says hello",
            "maxSteps": 30
          }
        ]
      },
      "AgentStep": {
        "type": "object",
        "description": "One thing a run did, as the harness in the slot reported it.",
        "properties": {
          "seq": {
            "type": "integer",
            "minimum": 1
          },
          "at": {
            "$ref": "#/components/schemas/Millis"
          },
          "action": {
            "type": "string",
            "description": "The computer-use action (`left_click`, `type`, `key`, `scroll`, `screenshot`, \u2026), `finish`, or `note` when the model spoke without acting."
          },
          "input": {
            "type": "object",
            "description": "Its arguments as the model gave them; coordinates are pixels of the screenshot before it."
          },
          "ok": {
            "type": "boolean"
          },
          "error": {
            "type": "string"
          },
          "note": {
            "type": "string",
            "description": "What the model said before acting, cut short."
          },
          "frame": {
            "type": "string",
            "pattern": "^frame-\\d{4,}\\.png$",
            "description": "A `screenshot` step's picture: `GET /v1/runs/{runId}/frames/{frame}`."
          },
          "width": {
            "type": "integer",
            "minimum": 1,
            "description": "A `screenshot` step's picture's width in pixels: the space later steps' coordinates are in."
          },
          "height": {
            "type": "integer",
            "minimum": 1
          }
        },
        "required": [
          "seq",
          "at",
          "action",
          "ok"
        ],
        "additionalProperties": false
      },
      "AgentRun": {
        "type": "object",
        "description": "A goal handed to a gui sandbox, and what came of it.",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^run_"
          },
          "sandboxId": {
            "type": "string",
            "pattern": "^sbx_"
          },
          "goal": {
            "type": "string"
          },
          "maxSteps": {
            "type": "integer"
          },
          "maxDurationMs": {
            "type": "integer"
          },
          "model": {
            "$ref": "#/components/schemas/AgentModel"
          },
          "state": {
            "enum": [
              "running",
              "finished"
            ]
          },
          "startedAt": {
            "$ref": "#/components/schemas/Millis"
          },
          "endedAt": {
            "$ref": "#/components/schemas/Millis"
          },
          "verdict": {
            "enum": [
              "done",
              "failed",
              "incomplete"
            ],
            "description": "`done` and `failed` are the model's own verdict; `incomplete` means its steps or time ran out, or it broke first (see `error`)."
          },
          "reason": {
            "type": "string",
            "description": "The model's account of the verdict, or why the run ended without one."
          },
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string"
              },
              "message": {
                "type": "string"
              }
            },
            "required": [
              "code",
              "message"
            ],
            "additionalProperties": false,
            "description": "Set when the run broke rather than ended: the harness failed, the sandbox closed."
          },
          "steps": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AgentStep"
            }
          },
          "frames": {
            "type": "integer",
            "minimum": 0,
            "description": "Screenshots the run's recording holds."
          },
          "replayUrl": {
            "type": "string",
            "description": "Its sandbox's replay in the console, where this run's steps play back against the screen: present when the sandbox records."
          }
        },
        "required": [
          "id",
          "sandboxId",
          "goal",
          "maxSteps",
          "maxDurationMs",
          "model",
          "state",
          "startedAt",
          "steps",
          "frames"
        ],
        "additionalProperties": false,
        "examples": [
          {
            "id": "run_mh3k2f9aQ1xYz0aBcDeFgHiJ",
            "sandboxId": "sbx_mh3k2f9aQ1xYz0aBcDeFgHiJ",
            "goal": "TextEdit has a document saved as ~/Desktop/note.txt that says hello",
            "maxSteps": 30,
            "maxDurationMs": 600000,
            "model": "claude-opus-5-5",
            "state": "finished",
            "startedAt": 1793000000000,
            "endedAt": 1793000094000,
            "verdict": "done",
            "reason": "The file note.txt is on the Desktop and TextEdit shows \"hello\".",
            "steps": [
              {
                "seq": 1,
                "at": 1793000001000,
                "action": "screenshot",
                "input": {
                  "automatic": true
                },
                "ok": true,
                "frame": "frame-0001.png"
              },
              {
                "seq": 2,
                "at": 1793000009000,
                "action": "key",
                "input": {
                  "text": "super+space"
                },
                "ok": true,
                "note": "I'll open TextEdit with Spotlight."
              }
            ],
            "frames": 1
          }
        ]
      },
      "RecordingEntry": {
        "type": "object",
        "description": "One thing on a recording's timeline. `type` says which fields it has: `frame` (a picture the plane took: `frame`, `width`, `height`, `source`), `step` (an agent run's step, as in AgentStep, with its picture's name in this recording and `space`, the pixel space its coordinates are in), `run` (an agent run `started` with its `goal` and `model`, or `finished` with its `verdict`), `command` (a command a customer ran, with its exit and the tail of its output), `log` (a gui task's log lines) and `end` (why it ended).",
        "properties": {
          "seq": {
            "type": "integer",
            "minimum": 1,
            "description": "Its place on the timeline, from 1: what `live` resumes after."
          },
          "at": {
            "$ref": "#/components/schemas/Millis"
          },
          "type": {
            "type": "string",
            "enum": [
              "frame",
              "step",
              "run",
              "command",
              "log",
              "end"
            ]
          },
          "frame": {
            "type": "string",
            "description": "A picture: `GET /v1/recordings/{recordingId}/frames/{frame}`."
          },
          "width": {
            "type": "integer",
            "minimum": 1
          },
          "height": {
            "type": "integer",
            "minimum": 1
          },
          "source": {
            "type": "string",
            "enum": [
              "start",
              "command",
              "background",
              "task"
            ],
            "description": "Why the plane took the picture: the slot became ready, a command ended, a run was going, or a gui task's box sent it."
          },
          "runId": {
            "type": "string",
            "pattern": "^run_"
          },
          "action": {
            "type": "string"
          },
          "input": {
            "type": "object"
          },
          "ok": {
            "type": "boolean"
          },
          "error": {
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "object",
                "properties": {
                  "code": {
                    "type": "string"
                  },
                  "message": {
                    "type": "string"
                  }
                },
                "required": [
                  "code",
                  "message"
                ]
              }
            ]
          },
          "note": {
            "type": "string"
          },
          "space": {
            "type": "object",
            "properties": {
              "width": {
                "type": "integer"
              },
              "height": {
                "type": "integer"
              }
            },
            "required": [
              "width",
              "height"
            ],
            "additionalProperties": false
          },
          "event": {
            "type": "string",
            "enum": [
              "started",
              "finished"
            ]
          },
          "goal": {
            "type": "string"
          },
          "model": {
            "type": "string"
          },
          "verdict": {
            "type": "string",
            "enum": [
              "done",
              "failed",
              "incomplete"
            ]
          },
          "reason": {
            "type": "string"
          },
          "command": {
            "type": "string",
            "description": "The command line, cut to 2,000 characters. Its environment is never recorded."
          },
          "exitCode": {
            "type": [
              "integer",
              "null"
            ]
          },
          "timedOut": {
            "type": "boolean"
          },
          "durationMs": {
            "type": "integer",
            "minimum": 0
          },
          "output": {
            "type": "string",
            "description": "The last 8 KiB of its output."
          },
          "truncated": {
            "type": "boolean"
          },
          "lines": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        },
        "required": [
          "seq",
          "at",
          "type"
        ],
        "additionalProperties": false
      },
      "Recording": {
        "type": "object",
        "description": "A gui sandbox's or gui task's timeline, kept by the plane so it plays back after the slot is reset. Private to the team; deleted with its other artifacts.",
        "properties": {
          "id": {
            "type": "string",
            "description": "The sandbox's or task's id: a recording is named after what it recorded."
          },
          "kind": {
            "type": "string",
            "enum": [
              "sandbox",
              "task"
            ]
          },
          "name": {
            "type": "string"
          },
          "state": {
            "type": "string",
            "enum": [
              "recording",
              "ended"
            ]
          },
          "startedAt": {
            "$ref": "#/components/schemas/Millis"
          },
          "endedAt": {
            "$ref": "#/components/schemas/Millis"
          },
          "frames": {
            "type": "integer",
            "minimum": 0,
            "description": "Pictures the plane kept for it, not counting an agent run's own."
          },
          "truncated": {
            "type": "boolean",
            "description": "A cap stopped it keeping more: pictures, entries, or the team's artifact share."
          },
          "replayUrl": {
            "type": "string"
          },
          "entries": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/RecordingEntry"
            }
          }
        },
        "required": [
          "id",
          "kind",
          "state",
          "startedAt",
          "frames",
          "truncated",
          "replayUrl",
          "entries"
        ],
        "additionalProperties": false
      },
      "CreditGrant": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "`grant_\u2026`"
          },
          "reason": {
            "type": "string",
            "description": "What it is for: `free` for the sign-up credit, `yc` for the YC deal, or another short slug."
          },
          "grantedAt": {
            "$ref": "#/components/schemas/Millis"
          },
          "expiresAt": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/Millis"
              },
              {
                "type": "null"
              }
            ],
            "description": "When it stops covering new work; null for never."
          },
          "active": {
            "type": "boolean",
            "description": "Still covers new work: not expired, not revoked."
          },
          "grantedUsd": {
            "$ref": "#/components/schemas/Usd"
          },
          "usedUsd": {
            "$ref": "#/components/schemas/Usd"
          },
          "remainingUsd": {
            "$ref": "#/components/schemas/Usd",
            "description": "What it has left for new work; 0 once it is no longer active."
          }
        },
        "required": [
          "id",
          "reason",
          "grantedAt",
          "expiresAt",
          "active",
          "grantedUsd",
          "usedUsd",
          "remainingUsd"
        ],
        "additionalProperties": false
      }
    },
    "parameters": {
      "TaskId": {
        "name": "taskId",
        "in": "path",
        "required": true,
        "description": "A task id, `task_\u2026`.",
        "schema": {
          "type": "string"
        }
      },
      "SandboxId": {
        "name": "sandboxId",
        "in": "path",
        "required": true,
        "description": "A sandbox id, `sbx_\u2026`.",
        "schema": {
          "type": "string"
        }
      },
      "CommandId": {
        "name": "commandId",
        "in": "path",
        "required": true,
        "description": "The `commandId` from `runCommand`, `cmd_\u2026`.",
        "schema": {
          "type": "string"
        }
      },
      "ArtifactName": {
        "name": "name",
        "in": "path",
        "required": true,
        "description": "An artifact name the task promised.",
        "schema": {
          "type": "string"
        }
      },
      "UserId": {
        "name": "userId",
        "in": "path",
        "required": true,
        "description": "A user id, `usr_\u2026`.",
        "schema": {
          "type": "string"
        }
      },
      "KeyId": {
        "name": "keyId",
        "in": "path",
        "required": true,
        "description": "A key id, `key_\u2026` (not the secret).",
        "schema": {
          "type": "string"
        }
      },
      "Limit": {
        "name": "limit",
        "in": "query",
        "required": false,
        "description": "Page size.",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "maximum": 200,
          "default": 50
        }
      },
      "Cursor": {
        "name": "cursor",
        "in": "query",
        "required": false,
        "description": "The previous page's `nextCursor`. It marks a position among all your items, so an item that leaves the `state` filter between pages never breaks the listing.",
        "schema": {
          "type": "string"
        }
      },
      "TaskState": {
        "name": "state",
        "in": "query",
        "required": false,
        "description": "Only tasks in this state.",
        "schema": {
          "$ref": "#/components/schemas/TaskState"
        }
      },
      "TaskKind": {
        "name": "kind",
        "in": "query",
        "required": false,
        "description": "Only tasks of this kind. `runner` lists GitHub Actions jobs.",
        "schema": {
          "$ref": "#/components/schemas/TaskKind"
        }
      },
      "SandboxState": {
        "name": "state",
        "in": "query",
        "required": false,
        "description": "Only sandboxes in this state.",
        "schema": {
          "type": "string",
          "enum": [
            "pending",
            "ready",
            "closed"
          ]
        }
      },
      "Wait": {
        "name": "wait",
        "in": "query",
        "required": false,
        "description": "Hold the request up to this many seconds for it to end. 0 to 60 is accepted; the plane never holds more than 50 s.",
        "schema": {
          "type": "number",
          "minimum": 0,
          "maximum": 60,
          "default": 0
        }
      },
      "FilePath": {
        "name": "path",
        "in": "query",
        "required": true,
        "description": "Relative to the sandbox root (`/Users/<slot>/offstage-sandbox/<sandbox id>`): letters, digits, `.`, `_`, `-` and `/`, no `..` segment, no leading `/` or `~`, at most 1024 characters. `.` segments and repeated slashes are normalized away.",
        "schema": {
          "type": "string",
          "minLength": 1,
          "maxLength": 1024,
          "pattern": "^(?!/)(?!(.*/)?\\.\\.(/|$))[A-Za-z0-9._/-]+$"
        }
      },
      "IdempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": false,
        "description": "Any unique string. A retry with the same key and body within 24 hours answers 201 with what the first request made, as it is now, instead of creating again, including after a plane restart; the same key with a different body is 409 `idempotency-key-reused`. Failures are not remembered. Scoped to the team and the route.",
        "schema": {
          "type": "string",
          "minLength": 1,
          "maxLength": 255
        }
      },
      "InstallationId": {
        "name": "installationId",
        "in": "path",
        "required": true,
        "description": "A GitHub App installation id, as `getGitHub` lists it.",
        "schema": {
          "type": "string"
        }
      },
      "RunId": {
        "name": "runId",
        "in": "path",
        "required": true,
        "description": "A run id, `run_\u2026`, from `runAgent`.",
        "schema": {
          "type": "string"
        }
      },
      "RecordingId": {
        "name": "recordingId",
        "in": "path",
        "required": true,
        "description": "The sandbox's or task's id (`sbx_\u2026` or `task_\u2026`): its recording is named after it.",
        "schema": {
          "type": "string"
        }
      }
    },
    "headers": {
      "RequestId": {
        "description": "This request's id, `req_\u2026`. Quote it when asking for help.",
        "required": true,
        "schema": {
          "type": "string",
          "pattern": "^req_"
        }
      },
      "IdempotentReplayed": {
        "description": "`true` when this answer is a replay of an earlier request with the same `Idempotency-Key`.",
        "schema": {
          "type": "string",
          "enum": [
            "true"
          ]
        }
      },
      "SetSession": {
        "description": "Sets the `oc_session` cookie.",
        "schema": {
          "type": "string"
        }
      }
    },
    "responses": {
      "RateLimited": {
        "description": "`rate-limit`: this team has sent more than 120 requests in the last 60 s, or this client address more than 600 (fewer on the routes that need no credential). The `Retry-After` header gives the seconds left in the window.",
        "headers": {
          "x-request-id": {
            "$ref": "#/components/headers/RequestId"
          },
          "Retry-After": {
            "schema": {
              "type": "string"
            },
            "description": "Seconds until the window opens again."
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "BodyTooLarge": {
        "description": "The JSON body is over 1 MiB (a `command` is 64 KiB at most; the rest is spare).",
        "headers": {
          "x-request-id": {
            "$ref": "#/components/headers/RequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    }
  },
  "x-internal": {
    "description": "Routes this plane serves that are not part of the customer API, and so are not in `paths`: the fleet boxes' protocol (box tokens), the model proxy an agent run's harness calls (the run's own token, `omt_\u2026`), the operator's routes (the operator key), the payment provider's and GitHub's webhooks (signed, no credential), GitHub's redirect back to the console after its sign-in or install page (a browser page that redirects to `/ci`, or on to GitHub's install page), and the test-mode checkout page. A customer credential on a box or operator route gets 403 `box-only` or `operator-only`.",
    "routes": [
      "POST /v1/boxes",
      "GET /v1/boxes",
      "POST /v1/boxes/{boxId}/heartbeat",
      "POST /v1/boxes/{boxId}/claim",
      "POST /v1/boxes/{boxId}/sandboxes/{sandboxId}/ops",
      "POST /v1/boxes/{boxId}/sandboxes/{sandboxId}/ops/{opId}",
      "POST /v1/tasks/{taskId}/events",
      "PUT /v1/tasks/{taskId}/artifacts/{name}",
      "GET /v1/metering",
      "POST /v1/admin/billing/close",
      "PUT /v1/admin/billing/teams/{teamId}/ceiling",
      "PUT /v1/admin/billing/teams/{teamId}/rate",
      "GET /v1/admin/billing/teams",
      "POST /v1/admin/billing/teams/{teamId}/credit",
      "DELETE /v1/admin/billing/teams/{teamId}/credit/{grantId}",
      "GET /v1/admin/billing/credit",
      "GET /v1/admin/overview",
      "GET /v1/admin/activity",
      "POST /v1/billing/webhook",
      "GET /billing/test-checkout/{checkoutId}",
      "POST /billing/test-checkout/{checkoutId}",
      "POST /v1/github/webhook",
      "GET /v1/github/setup",
      "POST /v1/boxes/{boxId}/sandboxes/{sandboxId}/runs/{runId}/events",
      "POST /v1/model/messages"
    ]
  }
}
