{
  "openapi": "3.1.0",
  "info": {
    "title": "Glac API",
    "version": "0.1.0",
    "description": "This is the reference of the Glac API: every call, what it takes and what it answers. The [CLI](/ru/docs/cli/), the [SDKs](/ru/docs/) and the [MCP server](/ru/docs/mcp/) call it. [Download this description](/ru/docs/api/openapi.json) (OpenAPI 3.1).\n\nThe calls are arranged by product, as the [docs](/ru/docs/) are: Cube (its calls are `/v1/apps`: an app, which is what a Cube is, its versions and its instances), Sandbox, Drive (with the volumes underneath Drives), and then the platform's own calls: the project, keys, secrets, the account and its bill, events.\n\n**Address.** Every path is under `https://api.glac.ai`.\n\n**One API in front of many machines.** Sandboxes, and what is in them, live on machines (*nodes*, in the calls' own words). A call about a sandbox, a command, a file, a port's link, a snapshot or a volume is passed by the API to the machine that holds the thing, and a call that makes one to a machine the API picks; a list asks every machine and puts their answers together. What the machine answers comes back unchanged: that is what each of these calls lists. A project's secrets are kept by the API for all its machines, and answered in the same words. The API adds a few answers of its own to these calls, the same for each: [Calls passed to a machine](/ru/docs/api/#calls-passed-to-a-machine). The other calls (apps, projects, keys, spend limits, events, the account and its bill, the console) are the API's own.\n\n## Authentication\n\nEvery call but the console's sign-up and sign-in (`/v1/auth/…`) takes a credential, in `Authorization: Bearer <credential>`.\n\n- **A project's key** (`ak_…`). Make one in the console, on its API keys page, with the scopes and the expiry you choose, or with `POST /v1/keys`. The key is shown once, when it is made; `DELETE /v1/keys/{id}` revokes it at once. No key is 401 `missing_api_key`, a wrong one `invalid_api_key`, one past its expiry `expired_api_key`.\n- **A token for one sandbox** (`st_…`), from `POST /v1/sandboxes/{id}/tokens`: short-lived, with some of the scopes `url`, `exec`, `files` and `tty`. On a `GET` it may be the query parameter `sandbox_token` instead, for a browser, which cannot set headers on an `EventSource` or a WebSocket. The API passes a call made with a token to the machine of its sandbox, which checks the token: to a token every other sandbox is 404, and a call that takes a project's key is 403 `needs_api_key`.\n- **The console's session.** The console's own calls (`/v1/me…`, `/v1/accounts/{id}…`) take the cookie that `POST /v1/auth/sign-in` sets. With it, and `X-Account` and `X-Project` naming the account and the project, the console calls the rest of the API as a key of that project with every scope, within what the user's role in the account allows.\n\n**Scopes.** A key allows some of seven scopes: `read`, `write`, `exec`, `files`, `tty`, `url` and `admin` ([what each one opens](/ru/docs/platform/projects/#api-keys)). A key made without `scopes` has all of them but `admin`. Each call ends its description with the scope it takes (\"Takes `read`.\"), and a key without that scope is 403 `key_scope`, whose message names it.\n\n## Projects\n\nAn account has one or more projects, and a key belongs to one of them. Everything a call makes with it is that project's, and a call sees only that project's sandboxes, snapshots, volumes, secrets, apps and the rest: another project's are not there (404). Names are unique within a project, for each kind of thing.\n\nIds begin with what they name: `sbx-`, `exe-` (a command), `snap-`, `vol-`, `sshkey-`. Wherever a path, a field or the `sandbox_id` filter takes the id of a sandbox, a snapshot, a volume or an SSH key, it takes its name as well (but in the host name of a link to a port).\n\n## Idempotency keys\n\nAny `POST` may carry `Idempotency-Key` (1 to 255 characters). The same call again, with the same key and the same body, is answered as the first one was, with `Idempotent-Replayed: true`, and nothing is done twice. The same key with another body is 409 `idempotency_key_reused`; while the first call still runs, a second one waits up to 60 seconds, then is 409 `idempotency_in_progress`. An answer is kept for 24 hours; answers of 409 and of 5xx, and event streams, are not kept, and the call runs again. A command's key (`…/run`, `…/exec`) is kept with the command, for as long as the sandbox has it. A create sent again with its key goes to the machine the first one went to, and is given the size the first one was given, whatever the account's plan is by then.\n\n## Pagination\n\nA list takes `limit` (1 to 1000; 100 when not given) and `cursor`, and answers `{\"data\": […], \"next_cursor\": \"…\"}`, newest first (the entries of a directory, and secrets, by name). `next_cursor` is null on the last page; give it as `cursor` for the next one. A cursor names a position, not a page: it stays good while things are made and deleted.\n\n## Errors\n\nEvery error has one shape:\n\n```json\n{\"error\": {\"type\": \"not_found\", \"code\": \"sandbox_not_found\", \"message\": \"no sandbox sbx-…\",\n           \"param\": null, \"retryable\": false, \"request_id\": \"req-…\", \"docs_url\": null}}\n```\n\n`type` sets the status: `invalid_request` 400, `unauthenticated` 401, `payment_required` 402, `forbidden` 403, `not_found` 404, `conflict` 409, `too_large` 413, `too_many_requests` 429, `internal` 500, `bad_gateway` 502, `unavailable` 503, `timeout` 504. `code` says which failure it is: each call lists its codes under each of its statuses. `message` says it in words for people; `param` names the field or the query parameter at fault; `retryable` says whether the same call again may succeed, and a `Retry-After` header says when, where that is known. A field or a query parameter the call does not take is refused (400 `unknown_field`, `unknown_parameter`), and the message lists what it takes. `request_id` is the call's id, in the answer's `X-Request-Id` header too: send an `X-Request-Id` of your own (1 to 64 letters, digits, `.`, `_` and `-`) and it is used. A 500 says only that something failed: quote its `request_id`. `docs_url`, where an error has one, leads to its code on the [errors](/ru/docs/errors/) page, which says what to do about the refusals that send you there: a plan's cap, a project's Drives and volumes, an account on hold, a locked user.\n\n## Event streams and terminals\n\nA command's output can be read as a stream of events (`text/event-stream`): from `POST /v1/run` and `POST /v1/sandboxes/{id}/run` with `stream: true` (or `Accept: text/event-stream`), and from `GET /v1/sandboxes/{id}/exec/{exec_id}/output`. Each event has an `event:` line, for output an `id:` line (the position after the event's last byte, stdout and stderr counted together) and one `data:` line of JSON. A comment, `: waiting`, comes every 15 seconds while nothing happens. A reader that falls behind what the sandbox still keeps is told so with `gap`. To read on after a lost connection, ask `…/exec/{exec_id}/output` with `Last-Event-ID` (or `from`).\n\nThe events:\n\n- `start`: First event of a stream that started the command. Its data: `{id, \"object\": \"exec\", sandbox_id, command}`.\n- `stdout`: A piece of standard output. `id` is the position after its last byte. Its data: `{text}` or `{base64}`.\n- `stderr`: A piece of standard error. `id` is the position after its last byte. Its data: `{text}` or `{base64}`.\n- `gap`: Output is no longer kept: what the reader asked for, or what it was about to get. The stream goes on from `available_from`. Its data: `{from, available_from}`.\n- `exit`: Last event: the command has ended. `id` is the position after the last byte of its output. Its data: `{id, \"object\": \"exec\", sandbox_id, command, tty, status, …}` (as `GET /v1/sandboxes/{id}/exec/{exec_id}` answers it).\n- `end`: Last event of a stream asked for with `follow=false` while the command is still running. `id` is the position reached. Its data: `{\"status\": \"running\", offset}`.\n- `error`: Last event: the command was lost (the sandbox was restarted from its disk, restored or deleted), or the sandbox was moved to another node with its processes, where the command goes on: `sandbox_moved` (retryable), with `to`, `epoch` and `offset`, the id of the event before it; the same stream asked for again through the control plane with `Last-Event-ID` (or `from`) set to `offset` goes on there, missing nothing and repeating nothing. Its data is the error object of every other error. Its data: `{error, …}`, [the error](/ru/docs/api/#errors).\n\nA terminal (`GET /v1/sandboxes/{id}/exec/{exec_id}/tty`) is a WebSocket. Binary messages are bytes: what is typed one way, what the terminal shows the other. Text messages are JSON:\n\n- `{\"type\": \"eof\"}`: Client to server, as a text message: the client sends no more. The guest half-closes its connection to the peer; what the peer still sends comes back.\n- `{\"type\": \"listening\", path}`: Server to client, the first message: where the socket is in the sandbox.\n- `{\"type\": \"reset\"}`: Server to client: every connection to the socket is gone (the sandbox went to sleep); the socket stays.\n- `{\"type\": \"resize\", cols, rows}`: Client to server, as a text message: the terminal has this size now. Out-of-range sizes are dropped without an answer.\n- `{\"type\": \"signal\", …}`: Client to server, as a text message: send the command's process group a signal.\n- `{\"type\": \"exit\", status, exit_code, signal}`: Server to client, as a text message: the command has ended. A close frame (1000) follows.\n- `{\"type\": \"error\", error, …}`: Server to client, as a text message: the command was lost (the sandbox was restarted, restored or deleted), or the sandbox was moved to another node with its processes, where the command goes on (`sandbox_moved`, retryable, with `to`, `epoch` and `offset`: join again through the control plane with `from` set to `offset`), or a key or a message of the client's was refused (`spend_limit_reached`, say). `error` is the error object of every other error. A close frame follows: 1012 with the reason `sandbox_moved` after `sandbox_moved`, 1000 otherwise. A key typed while the sandbox is being moved waits for the move's end: it goes in where the move failed.\n- `{\"type\": \"gap\", from, available_from}`: Server to client, as a text message: the client fell further behind than the sandbox keeps of a command's output (the last 16 MiB), and what the terminal shows next begins at `available_from`, not where it stood. Sent before what follows it, as a stream's `gap` event is. The bytes a terminal shows carry no position: a client that counts them, to join again with `from` after its connection was lost (the SSH gateway, whose sessions outlive a restart of the node's daemon), goes on counting from `available_from`.\n\n## Calls passed to a machine\n\nWhat the machine that holds a thing answers is what each of these calls lists. The API answers some of them itself, before any machine is asked or in place of the machine's answer:\n\n- **400** `invalid_json`: Of `POST /v1/sandboxes`, `/v1/run` and `/v1/volumes`: the body is not a JSON object (no body is `{}`). Refused before any machine is asked, whether or not one is there.\n- **400** `unknown_field`: A create that gives `project_address`: the address of a sandbox on its project's network is the API's to give. Of a sandbox's `drives`, a field a mount does not have. A create or a change of a sandbox that is no Browser session that gives it a `proxy`: a proxy is a session's (`POST /v1/browsers`, `PATCH /v1/browsers/{id}`).\n- **400** `unknown_region`: A create whose `region` is not one of the regions.\n- **400** `invalid_field`: A volume made from a backup (`from_backup`) that also gives `size_bytes`, `shared` or `from_backup_index` (it has the size and the kind of the volume the backup was taken of), or a `region` other than the backup's. Of a sandbox's `drives`: no list, a `path` that is not absolute, a `mode` other than `read_write` (the read-only mount of a Drive's snapshot comes later).\n- **400** `missing_field`: A sandbox's create whose `drives` names a Drive without its `drive` or its `path` (`param` `drives[i].drive`, `drives[i].path`).\n- **400** `reserved_metadata`: A sandbox's create (`POST /v1/sandboxes`, `/v1/run`) or change (`PATCH /v1/sandboxes/{id}`) whose `metadata` names the key `product` (`param` `metadata.product`): what a sandbox is sold as (a Browser session) is the API's own record, and no metadata says it.\n- **400** `unknown_parameter`: A list that no machine answers (none is there, or none can be reached), which the API answers from what it last saw, given a query parameter the list does not take.\n- **402** `account_in_debt`: The account owes for what it stores past its plan. Every call is refused, before the key's scope is looked at, but reading the account, its bill, its usage and its backups, paying, making a volume from a backup, and (unless the API is run otherwise) listing, reading and deleting the sandboxes, snapshots and volumes it stores. The message says how much is owed and how to pay.\n- **403** `key_scope`: The key does not allow the scope the call takes; the message names it.\n- **403** `account_held`: The account is held for its payments: every call of its keys and of its sandboxes' tokens but `GET /v1/account`. The message says to write to support.\n- **403** `spend_limit_reached`: The project, or the sandbox, is at its spend limit: no sandbox, fork, one-shot run, volume (but one made from a backup) or snapshot is made, and a sandbox the limit froze is not woken.\n- **403** `balance_used_up`: The CPU time or the memory the account's plan includes is used up, and the account has no balance to pay for more: the same calls are refused.\n- **403** `plan_size_exceeded`: Larger than the account's plan lets a sandbox be: the `limits` asked for (`param` `limits.vcpus`, `limits.memory_bytes`, or `limits` for both), or the shape of the snapshot or template it is made from, forked from or rolled back to. A sandbox that names no size is made as large as the plan allows at most.\n- **403** `plan_cap_reached`: More than the account may have at once (`param` names which; `GET /v1/account` has them under `caps`): sandboxes awake, their vCPUs or their memory (of a create, a one-shot run, a fork, or a call that wakes a sandbox), sandboxes asleep, or sandboxes made in the last minute or hour (these are `retryable`, with `Retry-After`). A sleep and a delete are never refused.\n- **403** `volume_limit_reached`: Of `POST /v1/volumes`: one volume more, a larger one, or more in all than the plan lets a project keep (`GET /v1/billing` has the numbers).\n- **403** `drive_limit_reached`: Of a sandbox's snapshot that carries Drives: a sandbox made from it (`POST /v1/sandboxes` and `POST /v1/run` with `snapshot`), each copy of a fork of a sandbox that has Drives, and a sandbox taken back to it get a new Drive of each, and are refused before any machine is asked where those would take the project past the Drives its plan lets it keep (how many, how large each, how large together), as a fork through `POST /v1/drives/{id}/fork` is. `param` is `snapshot` of a create and a roll-back; the message says the plan's numbers, what the project has and what this would make.\n- **403** `secret_limit_reached`: Of `PUT /v1/secrets/{name}`: a secret more than the plan lets a project keep, a value larger than one may be (`param` `value`), or one that would give an app's instances more of the project's secrets than one sandbox may be given.\n- **403** `proxy_not_on_plan`: A change of a Browser session (`PATCH /v1/sandboxes/{id}`) that gives it a `proxy` on the free plan, which has none.\n- **403** `account_share_reached`: Something new of the account (a sandbox, a one-shot run, a fork's copies, a snapshot, a volume or Drive) would take it past its share of the data drive of the machine it would be on: one account holds at most a share of a machine's drive (a quarter unless the machine is set otherwise). The API puts what is new on a machine where the account has the room, and passes this on when none has.\n- **403** `storage_quota_reached`: Of an account on Free that keeps as much as Free lets it keep at once (15 GB of volume data: what its volumes hold, its snapshots and sleeping sandboxes keep): a new volume or snapshot is refused (`param` `storage`) until it keeps less, or is on Paid.\n- **404** `backup_not_found`: A volume made from a backup that the project does not have (`param` `from_backup`).\n- **404** `drive_not_found`: A sandbox's create whose `drives` names a Drive the project does not have (`param` `drives[i].drive`).\n- **404** `snapshot_not_found`: A sandbox's create whose `drives` mounts a snapshot of a Drive the project does not have (`param` `drives[i].snapshot`).\n- **409** `not_on_one_machine`: A sandbox and a volume it is to have, or a shared volume that the snapshot it is made from or rolled back to mounts, are on different machines. A volume no sandbox has had yet moves to the sandbox's machine instead. Of a sandbox's `drives`: the Drives it mounts, and the snapshots of Drives it mounts read-only, are of more than one region, or of another region than the create names (`param` `region`); a snapshot is read in the region its Drive was in when it was taken, whether or not the Drive was deleted since.\n- **409** `snapshot_not_replicated`: A sandbox is to be made from, or rolled back to, a snapshot on another machine than the one that took it (that one is away, or the volumes are elsewhere), and the snapshot is not in the object store yet: its `last_replication_status` says how its copy stands. `param` is `snapshot`.\n- **409** `not_loadable`: The machine the call goes to cannot make machines from the snapshot as it was published (another build of Firecracker, other settings of the devices, a larger shape, another size of disk); the message says what differs.\n- **409** `backup_not_ready`, `backup_unusable`: A volume made from a backup that is still being made, or that failed.\n- **409** `too_many_drives`, `key_drive`, `duplicate_volume`, `drive_attached`: Of a sandbox's create with `drives`: more than 8 Drives and volumes together (`too_many_drives`); a keyed instance's Drive (`key_drive`); one Drive named twice (`duplicate_volume`); a Drive another sandbox has, which stays where it is (`drive_attached`).\n- **409** `not_copied`, `store_unfenced`: Of a sandbox's create with `drives`: a Drive on another machine than the one the sandbox can be made on, where that machine copies nothing to an object store (`not_copied`), or copies to one that no move goes through (`store_unfenced`).\n- **409** `managed_by_database`: A sandbox's create (`POST /v1/sandboxes`, `/v1/run`), or a change of its mounts (`PATCH /v1/sandboxes/{id}` with `volumes`), that mounts a database's Drive read-write (`param` names it); a delete, a change or a restore of a sandbox that is a database's instance; any change of a volume that is a database's Drive. What a database is made of is changed through `/v1/databases` alone.\n- **409** `managed_by_installation`: The same of what an installation of a Managed Application is made of: a sandbox's create, or a change of its mounts, that mounts an installation's Drive read-write; a delete, a change or a restore of a sandbox that is an instance of an installation's app; any change of a volume that is an installation's Drive. What an installation is made of is changed through `/v1/installations` alone.\n- **409** `checkpoints_unavailable`: A sandbox with `checkpoint` on (its own setting, or a keyed instance's from its app) where no machine takes checkpoints, or where what it is made from is on a machine that does not; and a `PATCH` that turns them on, on a machine that does not. `param` is `checkpoint`. While every machine that takes them is away or full, the create is 503 `no_capacity` instead, `retryable`.\n- **429** `quota_used_up`: Of an account on Free that has used up today's quota (UTC) of the CPU time or the memory the call would use: a create, a one-shot run, a fork, or a call that would wake a sandbox is refused until 00:00 UTC, whatever the account holds (`Retry-After` says the seconds until then).\n- **502** `fetch_failed`: A sandbox made from, or rolled back to, a snapshot on another machine than the one that took it: that machine could not bring the snapshot from the object store. The message has its words.\n- **503** `node_unavailable`: The machine that holds the thing cannot be reached, or does not have the project's latest secrets yet: nothing is lost, and the same call will do once it is back (`retryable`).\n- **503** `no_capacity`: No machine has room for a new sandbox or volume (of a volume made from a backup: no machine of the backup's region makes them).\n- **503** `moving`: The sandbox is moving to another machine: every call for it until the move is over (`retryable`, `Retry-After: 1`).\n- **503** `upgrading`: The sandbox is a keyed instance its machine is moving over to another version of its app (`POST /v1/apps/{app}/instances/{key}/upgrade`): every call for it until the new version is up, or the instance is put back (`retryable`, `Retry-After: 1`).\n- **503** `creation_cancelled`, `creation_cleanup_failed`: The machine made the thing and the API could not record it: what the call made was deleted again (`creation_cancelled`), or not all of it could be (`creation_cleanup_failed`: the message lists what is left, which is billed until it is deleted).\n- **503** `creation_replay_incomplete`, `creation_state_unavailable`, `creation_replay_record_failed`: A create sent again with its `Idempotency-Key`, whose first answer the machine gives again: what that answer named is no longer all there (`creation_replay_incomplete`; another key makes it anew), the machine could not be asked (`creation_state_unavailable`, `retryable`), or it is all there and the API could not record it (`creation_replay_record_failed`).\n- **503** `copy_behind`, `drive_not_brought`, `not_served_here`, `unavailable`: Of a sandbox's `drives`, each brought first to the machine the sandbox is made on: the object store does not hold the Drive as it is yet (`copy_behind`); its machine did not let it go, or the other could not take it (`drive_not_brought`); no machine of the region that could take it keeps Drives (`not_served_here`); another call is bringing it (`unavailable`). Each is `retryable` but `not_served_here`.\n- **503** `standby`: This copy of the API is not the one that serves: the answer names the one that does (`active`). `retryable`, with `Retry-After`.\n\nA sandbox, snapshot or volume whose machine cannot be reached is still listed and read, as the API last saw it, with `status` `unavailable` (of a sandbox, `awake_because` and `sleep_tier` are null). The creates take `region` (`na`, `eu`, `ap`), which the API reads itself and does not pass on; a sandbox's and a volume's `region` says where it is."
  },
  "servers": [
    {
      "url": "https://api.glac.ai",
      "description": "The API"
    }
  ],
  "tags": [
    {
      "name": "Cube",
      "description": "Cube, each of whose Cubes the API calls an app: an app, its versions, its keyed instances and its log; the domains that lead to an app; and where the apps' logs are exported to."
    },
    {
      "name": "Apps",
      "description": "Apps and their versions.",
      "parent": "Cube"
    },
    {
      "name": "Domains",
      "description": "Customers' own domains.",
      "parent": "Cube"
    },
    {
      "name": "Destinations",
      "description": "Where the logs of the project's apps are exported to: OpenTelemetry endpoints (OTLP over HTTP), which every node that keeps a log of one of them sends its lines to.",
      "parent": "Cube"
    },
    {
      "name": "Sandboxes",
      "description": "Creating sandboxes, changing their settings, putting them to sleep, restarting and copying them, and the credentials and links for one sandbox."
    },
    {
      "name": "Commands",
      "description": "A command belongs to its sandbox, not to the request that started it: it has an id, runs on when the caller goes away, and its output can be read from any position. `run` starts one and waits for the result; `exec` starts one and returns its handle.",
      "parent": "Sandboxes"
    },
    {
      "name": "Files",
      "description": "Files in a sandbox, through its agent rather than its network.",
      "parent": "Sandboxes"
    },
    {
      "name": "Snapshots",
      "description": "A snapshot is a sandbox's memory, processes and disk at one instant. A sandbox created from it, or rolled back to it, goes on from there.",
      "parent": "Sandboxes"
    },
    {
      "name": "SSH keys",
      "description": "The SSH public keys that open a project's sandboxes, which the control plane keeps for every node, and the SSH gateway's question.",
      "parent": "Sandboxes"
    },
    {
      "name": "Volumes",
      "description": "Volumes: disks of a project's that outlive the sandboxes that have them, each a block device with its own ext4, kept whole in the object store and on its node's NVMe as far as its cache goes (`cache_bytes`), and mounted read-write by one sandbox at a time, on any node of its region. A snapshot of a volume is a version of it in the object store, nothing copied: taken (`POST /v1/volumes/{id}/snapshots`), forked into a new volume, restored to, and mounted read-only by any number of sandboxes at once (`mode` `snapshot`); it outlives its volume, and is billed for what it keeps alone. A volume was called a Drive until 2026-10-28: `/v1/drives` (and `/v1/sandboxes/{id}/drives`, `/v1/drive-snapshots`) answer the same calls as they did, for the clients from before."
    },
    {
      "name": "Backups",
      "description": "The backups of a project's volumes: a copy of a volume at one instant in the object store, the project's until it expires or is deleted, whatever becomes of its volume; a new volume is made from one (`POST /v1/volumes` with `from_backup`).",
      "parent": "Volumes"
    },
    {
      "name": "Managed Applications",
      "description": "The catalogue of Managed Applications, apps hosted on Cube and billed by the Cube resources they use (reports/platform/managed-apps.md), and the project's installations of them (control/src/catalogue.rs, control/src/installations.rs)."
    },
    {
      "name": "Project",
      "description": "The project of the key: what it is, its private network, and its shared variables."
    },
    {
      "name": "Secrets",
      "description": "Named values the node keeps so that code in a sandbox can use them without holding them: a sandbox's `egress.inject` names a secret, and the node puts its value into the header of a request on the request's way out. A value is given and never given back.",
      "parent": "Project"
    },
    {
      "name": "Keys",
      "description": "A project's keys, and what each lets it do.",
      "parent": "Project"
    },
    {
      "name": "Events",
      "description": "What happened to a project's things and to its account: the feed, a page at a time or as a stream.",
      "parent": "Project"
    },
    {
      "name": "Webhooks",
      "description": "Where a project's events are posted, signed, and how they are sent again.",
      "parent": "Project"
    },
    {
      "name": "Audit",
      "description": "Who changed what in a project, when and from where.",
      "parent": "Project"
    },
    {
      "name": "Account and billing",
      "description": "The account and what it used; its plan, its bill and its Credits; and the spend limits of a project and of its sandboxes."
    },
    {
      "name": "Account",
      "description": "Two calls of the nodes' API that the control plane answers with more than a node does: the account of the key's project, and what its sandboxes, volumes and snapshots used.",
      "parent": "Account and billing"
    },
    {
      "name": "Billing",
      "description": "An account's plan and its subscription, what the plan includes and what the account owes, and its statements; the Credits it holds, its charges and its ledger.",
      "parent": "Account and billing"
    },
    {
      "name": "Limits",
      "description": "The spend limits of a project and of its sandboxes.",
      "parent": "Account and billing"
    },
    {
      "name": "Console",
      "description": "The web console and its users (control/README.md, 控制台：用户、会话、成员和网页): signing up, verifying an address, signing in and a new password by mail (`/v1/auth/*`, which need no session); the signed-in user's own (`/v1/me`); the accounts it is a member of, with their projects, members, invitations and record (`/v1/accounts/{id}`); and the console's page and files (`/console/`). A control plane started with `--console false` has none of them: each is 404 `no_such_route`, and the session's cookie opens nothing."
    },
    {
      "name": "Connected apps",
      "description": "Apps and agents a user connects to Glac with OAuth (control/README.md, 应用用 OAuth 连上 Glac): Ory Hydra is the authorization server (`--connect-issuer`), and leaves signing in and consenting to the console. Its metadata, a client's registration (RFC 7591) and the check of a client's metadata document are the control plane's, at the authorization server's name; the browser comes to `/v1/auth/connect/*` to sign in and to consent; the user's connected apps are `/v1/me/connections…`; an app's access token calls the API (`connectedApp`). A control plane without an authorization server has none of these calls: each is 404 `no_such_route`."
    },
    {
      "name": "Other",
      "description": "Calls in none of the groups above."
    },
    {
      "name": "Operator",
      "description": "The operator's calls, with the operator's key: accounts and their projects, nodes, edges, apps, charges, top-ups and Credits, debts and notices, the store's backups, and the control plane itself.",
      "parent": "Other"
    },
    {
      "name": "Browsers",
      "description": "Browsers sessions (reports/browsers/README.md, BR2): a managed Chromium, driven from outside over CDP (Playwright, Puppeteer). A session is a sandbox of the node's browser image, 1 vCPU and 2 GiB, marked as a session in the control plane's own table; awake while it is used, asleep with its pages when it is not, woken by the next CDP message.",
      "parent": "Other"
    },
    {
      "name": "Databases",
      "description": "Managed Postgres (reports/databases/README.md): an app of one stateful service and a Drive under each database, changed through this API alone.",
      "parent": "Other"
    }
  ],
  "paths": {
    "/v1/run": {
      "post": {
        "operationId": "runOnce",
        "tags": [
          "Sandboxes"
        ],
        "summary": "Run one command in a sandbox made for it",
        "description": "Creates a sandbox, runs the command in it, answers with the result and deletes the sandbox. The sandbox lives exactly as long as the request: it goes when the command ends and when the caller hangs up. The command gets 300 seconds unless `timeout_seconds` says otherwise. The call waits for the command however long it runs: there is no 202 here. A sandbox that was given a `name` has left the node's table, and its name is free, before the answer says that the command has ended (the JSON answer, or the `exit`, `error` or `end` event of a stream): the next call can give the same name at once.\n\nAs a stream (`text/event-stream`): its events are `start`, `stdout`, `stderr`, `gap`, `exit` and `error`: [Event streams and terminals](/ru/docs/api/#event-streams-and-terminals).\n\nTakes `write` and `exec`.",
        "security": [
          {
            "apiKey": [
              "write",
              "exec"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/NodeIdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RunOnceRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The command has ended: its result as JSON, or the event stream that led there. `sandbox_id` names the sandbox, which no longer exists.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/Idempotent-Replayed"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExecResult"
                }
              },
              "text/event-stream": {
                "schema": {
                  "type": "string",
                  "description": "When `stream` is true, or `Accept` asks for `text/event-stream`. Its events are `start`, `stdout`, `stderr`, `gap`, `exit` and `error`: [Event streams and terminals](/ru/docs/api/#event-streams-and-terminals)."
                },
                "x-events": [
                  "start",
                  "stdout",
                  "stderr",
                  "gap",
                  "exit",
                  "error"
                ]
              }
            }
          },
          "400": {
            "description": "The request is not one this call takes. `invalid_json`: the body is not a JSON object; `unknown_field`: the body has a field this call does not take; `unknown_parameter`: the query has a parameter this call does not take; `invalid_field`: a field or query parameter has the wrong type or is out of range; `invalid_name`: `name` is not a valid name; `invalid_env`: `env` has a name or a value an environment cannot hold; `invalid_metadata`: `metadata` is over its limits; `expires_in_the_past`: `expires_at` is not in the future; `invalid_egress`: `egress` is not a set of rules: `param` names the part that is wrong; `invalid_start`: `start`, `start_command` and `ready` do not go together as given (`ready` without a start command, `start: false` with `start_command`), or one of them is given with `init: \"systemd\"`, whose systemd starts what its units say, or `init: \"systemd\"` names no `image` on a node that has no image of a whole system (`--systemd-image`), or the sandbox is made from a `snapshot` or a `template`: `param` names the field; `ambiguous_source`: more than one of `image`, `snapshot` and `template`; `invalid_image`: `image` is not a reference to an image in a registry on the internet; `invalid_path`: a volume's `path` is not absolute or is `/`; `missing_command`: no `command`; `no_such_user`: `user` is not a user of the sandbox; `invalid_idempotency_key`: `Idempotency-Key` is empty or over 255 characters.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/Idempotent-Replayed"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter",
              "invalid_field",
              "invalid_name",
              "invalid_env",
              "invalid_metadata",
              "expires_in_the_past",
              "invalid_egress",
              "invalid_start",
              "ambiguous_source",
              "invalid_image",
              "invalid_path",
              "missing_command",
              "no_such_user",
              "invalid_idempotency_key",
              "guest_mitigations_fixed",
              "invalid_inbound"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "description": "The credential may not do this. `needs_api_key`: a sandbox token cannot do this. `spend_limit_reached`: a spend limit was reached. `secret_host_not_allowed`: a secret would be sent to a host it may not go to: by `egress.inject`, or as `image_pull_secret` to the `image`'s registry (`param` says which; `allowed_hosts` beside `error` says where it may go). `account_share_reached`: its account holds as much of this node's data drive as one account may (`drive.accounts` of `GET /internal/node`), and has not the room for the sandbox's memory and a gigabyte to write. Nothing was made.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/Idempotent-Replayed"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "needs_api_key",
              "spend_limit_reached",
              "secret_host_not_allowed",
              "account_share_reached"
            ]
          },
          "404": {
            "description": "Something the request names does not exist. `snapshot_not_found`: no such snapshot; `template_not_found`: no such template; `volume_not_found`: a volume named in `volumes` does not exist; `secret_not_found`: a secret named in `egress.inject`, or the one `image_pull_secret` names, does not exist.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/Idempotent-Replayed"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "snapshot_not_found",
              "template_not_found",
              "volume_not_found",
              "secret_not_found"
            ]
          },
          "409": {
            "description": "The state of something stands in the way. `name_taken`: the name is taken; `volume_in_use`: a volume in `volumes` that is not shared is another sandbox's; `duplicate_volume`: a volume is listed twice in `volumes`; `mount_path_taken`: two volumes in `volumes` have one `path`; `too_many_volumes`: `volumes` has more that are not shared than the sandbox's machine has spare drives (eight; two for one made from a template or a snapshot of before there were eight); `snapshot_lost`: the snapshot's `status` is `lost`; `no_project_address`: `project_network` was asked for, and the node has no network of the project, or no address of it left to spare; `command_lost`: the command went away while the call waited for it; `sandbox_failed`: the sandbox could not be started: the message says why (its image cannot be pulled, for one); `idempotency_key_reused`: the key was used with another body; `idempotency_in_progress`: the first request with the key is still running. `checkpoints_unavailable` (`param` `checkpoint`): `checkpoint` is on, and this node takes no checkpoints (it does not copy its disks to an object store, or its memory is not in layers).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "name_taken",
              "volume_in_use",
              "duplicate_volume",
              "mount_path_taken",
              "too_many_volumes",
              "snapshot_lost",
              "no_project_address",
              "command_lost",
              "sandbox_failed",
              "idempotency_key_reused",
              "idempotency_in_progress",
              "no_ipv6",
              "checkpoints_unavailable"
            ]
          },
          "503": {
            "description": "Not available. `not_served_here`: `egress` rules were given, and this node cannot put rules in force (it has no `nft`); or `project_network` was asked for, and this node puts no sandbox on a project's network; or the sandbox is to be made from an `image` the node has no template of, and this node fetches no images (`image_pulls` of `GET /internal/node` says why; `param` is `image`); or `key_missing`: this node is keyed (src/seal.rs) and does not hold the key of the project whose it is, or the platform's (the message says which): the control plane gives it, and the same call is made again (`Retry-After`); or `unavailable`: a volume in `volumes` could not be attached in time, and the same call is made again (`Retry-After`): the guest's mount of it did not finish within 60 s (a Drive whose blocks do not come: its object store away, or its fetcher out of reach), or it is still being taken off the machine after such an attach, or it is a Drive that comes block by block and nothing answers for its blocks yet.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "not_served_here",
              "key_missing",
              "unavailable"
            ]
          },
          "504": {
            "description": "The caller went away. `caller_gone`: the caller hung up.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "caller_gone"
            ]
          },
          "default": {
            "$ref": "#/components/responses/Failure"
          }
        }
      },
      "parameters": [
        {
          "$ref": "#/components/parameters/RequestId"
        }
      ]
    },
    "/v1/sandboxes": {
      "get": {
        "operationId": "listSandboxes",
        "tags": [
          "Sandboxes"
        ],
        "summary": "List sandboxes",
        "description": "Newest first. Wakes nothing. The filters are exact matches and combine with AND; `metadata.<key>=<value>` filters on a metadata key and may be given for several keys.\n\nTakes `read`.",
        "security": [
          {
            "apiKey": [
              "read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/NodeLimit"
          },
          {
            "$ref": "#/components/parameters/NodeCursor"
          },
          {
            "name": "status",
            "in": "query",
            "description": "Only sandboxes with this `status`. A value that is no status matches nothing.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "name",
            "in": "query",
            "description": "Only the sandbox with this name.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "One page.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SandboxList"
                }
              }
            }
          },
          "400": {
            "description": "The request is not one this call takes. `unknown_parameter`: the query has a parameter this call does not take; `invalid_field`: a field or query parameter has the wrong type or is out of range; `invalid_cursor`: `cursor` is not base64.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter",
              "invalid_field",
              "invalid_cursor"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "description": "The credential may not do this. `needs_api_key`: a sandbox token cannot do this.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "needs_api_key"
            ]
          },
          "default": {
            "$ref": "#/components/responses/Failure"
          }
        },
        "x-query-patterns": [
          {
            "pattern": "^metadata\\..+$",
            "schema": {
              "type": "string"
            },
            "description": "`metadata.<key>=<value>`: only sandboxes whose metadata has this value for this key."
          }
        ]
      },
      "post": {
        "operationId": "createSandbox",
        "tags": [
          "Sandboxes"
        ],
        "summary": "Create a sandbox",
        "description": "Returns when the sandbox can run commands, with `status` `running`. For an image the node has not built yet it returns at once with `status` `starting`; calls that need the sandbox wait for it, and if the image cannot be pulled the sandbox becomes `failed`.\n\nTakes `write`.",
        "security": [
          {
            "apiKey": [
              "write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/NodeIdempotencyKey"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SandboxCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The sandbox.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Location": {
                "$ref": "#/components/headers/Location"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/Idempotent-Replayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Sandbox"
                }
              }
            }
          },
          "400": {
            "description": "The request is not one this call takes. `invalid_json`: the body is not a JSON object; `unknown_field`: the body has a field this call does not take; `unknown_parameter`: the query has a parameter this call does not take; `invalid_field`: a field or query parameter has the wrong type or is out of range; `invalid_name`: `name` is not a valid name; `invalid_env`: `env` has a name or a value an environment cannot hold; `invalid_metadata`: `metadata` is over its limits; `expires_in_the_past`: `expires_at` is not in the future; `invalid_egress`: `egress` is not a set of rules: `param` names the part that is wrong; `invalid_start`: `start`, `start_command` and `ready` do not go together as given (`ready` without a start command, `start: false` with `start_command`), or one of them is given with `init: \"systemd\"`, whose systemd starts what its units say, or `init: \"systemd\"` names no `image` on a node that has no image of a whole system (`--systemd-image`), or the sandbox is made from a `snapshot` or a `template`: `param` names the field; `ambiguous_source`: more than one of `image`, `snapshot` and `template`; `invalid_image`: `image` is not a reference to an image in a registry on the internet; `invalid_path`: a volume's `path` is not absolute or is `/`; `invalid_idempotency_key`: `Idempotency-Key` is empty or over 255 characters.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/Idempotent-Replayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter",
              "invalid_field",
              "invalid_name",
              "invalid_env",
              "invalid_metadata",
              "expires_in_the_past",
              "invalid_egress",
              "invalid_start",
              "ambiguous_source",
              "invalid_image",
              "invalid_path",
              "invalid_idempotency_key",
              "guest_mitigations_fixed",
              "invalid_inbound",
              "invalid_proxy"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "description": "The credential may not do this. `needs_api_key`: a sandbox token cannot do this. `secret_host_not_allowed`: a secret would be sent to a host it may not go to: by `egress.inject` (the rules given, or a snapshot's), or as `image_pull_secret` to the `image`'s registry (`param` says which; `allowed_hosts` beside `error` says where it may go). `account_share_reached`: its account holds as much of this node's data drive as one account may (`drive.accounts` of `GET /internal/node`), and has not the room for the sandbox's memory and a gigabyte to write; the message says what it holds there and what it may. Nothing was made.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/Idempotent-Replayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "needs_api_key",
              "secret_host_not_allowed",
              "account_share_reached"
            ]
          },
          "404": {
            "description": "Something the request names does not exist. `snapshot_not_found`: no such snapshot; `template_not_found`: no such template; `volume_not_found`: a volume named in `volumes` does not exist; `secret_not_found`: a secret named in `egress.inject`, or the one `image_pull_secret` names, does not exist.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/Idempotent-Replayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "snapshot_not_found",
              "template_not_found",
              "volume_not_found",
              "secret_not_found"
            ]
          },
          "409": {
            "description": "The state of something stands in the way. `name_taken`: the name is taken; `volume_in_use`: a volume in `volumes` that is not shared is another sandbox's; `duplicate_volume`: a volume is listed twice in `volumes`; `mount_path_taken`: two volumes in `volumes` have one `path`; `too_many_volumes`: `volumes` has more that are not shared than the sandbox's machine has spare drives (eight; two for one made from a template or a snapshot of before there were eight); `snapshot_lost`: the snapshot's `status` is `lost`; `no_project_address`: `project_network` was asked for, and the node has no network of the project, or no address of it left to spare, or another sandbox took the one `project_address` names meanwhile; `sandbox_failed`: the sandbox's template holds its start command back (`TemplateBuildRequest.start_after_volumes`) and the command did not come up once the volumes were mounted: the message says what it printed last, and nothing is kept; `idempotency_key_reused`: the key was used with another body; `idempotency_in_progress`: the first request with the key is still running. `volume_restoring`: a volume it names is being made from a backup, or could not be. `checkpoints_unavailable` (`param` `checkpoint`): `checkpoint` is on, and this node takes no checkpoints (it does not copy its disks to an object store, or its memory is not in layers).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "name_taken",
              "volume_in_use",
              "duplicate_volume",
              "mount_path_taken",
              "too_many_volumes",
              "snapshot_lost",
              "no_project_address",
              "sandbox_failed",
              "idempotency_key_reused",
              "idempotency_in_progress",
              "volume_restoring",
              "no_ipv6",
              "checkpoints_unavailable"
            ]
          },
          "503": {
            "description": "Not available. `not_served_here`: `egress` rules were given, and this node cannot put rules in force (it has no `nft`); or `project_network` was asked for, and this node puts no sandbox on a project's network; or the sandbox is to be made from an `image` the node has no template of, and this node fetches no images (`image_pulls` of `GET /internal/node` says why; `param` is `image`); or `key_missing`: this node is keyed (src/seal.rs) and does not hold the key of the project whose it is, or the platform's (the message says which): the control plane gives it, and the same call is made again (`Retry-After`); or `unavailable`: a volume in `volumes` could not be attached in time, and the same call is made again (`Retry-After`): the guest's mount of it did not finish within 60 s (a Drive whose blocks do not come: its object store away, or its fetcher out of reach), or it is still being taken off the machine after such an attach, or it is a Drive that comes block by block and nothing answers for its blocks yet.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "not_served_here",
              "key_missing",
              "unavailable"
            ]
          },
          "default": {
            "$ref": "#/components/responses/Failure"
          }
        }
      },
      "parameters": [
        {
          "$ref": "#/components/parameters/RequestId"
        }
      ]
    },
    "/v1/sandboxes/{id}": {
      "get": {
        "operationId": "getSandbox",
        "tags": [
          "Sandboxes"
        ],
        "summary": "Look at a sandbox",
        "description": "Never waits and wakes nothing. A sandbox token reads its own sandbox with any scopes, without `url_secret` and `url_pattern`.\n\nTakes `read`, or a token of the sandbox.",
        "security": [
          {
            "apiKey": [
              "read"
            ]
          },
          {
            "sandboxToken": []
          },
          {
            "sandboxTokenInQuery": []
          }
        ],
        "responses": {
          "200": {
            "description": "The sandbox.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Sandbox"
                }
              }
            }
          },
          "400": {
            "description": "The request is not one this call takes. `unknown_parameter`: the query has a parameter this call does not take.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "404": {
            "description": "Something the request names does not exist. `sandbox_not_found`: no such sandbox.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "sandbox_not_found"
            ]
          },
          "default": {
            "$ref": "#/components/responses/Failure"
          }
        }
      },
      "delete": {
        "operationId": "deleteSandbox",
        "tags": [
          "Sandboxes"
        ],
        "summary": "Delete a sandbox",
        "description": "The machine, its disk and its saved memory go; its volumes are detached and kept; what it used stays in `/v1/usage`. Snapshots taken of it stay.\n\nTakes `write`.",
        "security": [
          {
            "apiKey": [
              "write"
            ]
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "The request is not one this call takes. `unknown_parameter`: the query has a parameter this call does not take.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "description": "The credential may not do this. `needs_api_key`: a sandbox token cannot do this.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "needs_api_key"
            ]
          },
          "404": {
            "description": "Something the request names does not exist. `sandbox_not_found`: no such sandbox.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "sandbox_not_found"
            ]
          },
          "409": {
            "description": "The call is refused. `static_instance`: the sandbox is the instance the node keeps of a static spec (`GET /internal/static`): it goes with its spec (`DELETE /internal/static/{name}`).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "static_instance"
            ]
          },
          "default": {
            "$ref": "#/components/responses/Failure"
          }
        }
      },
      "patch": {
        "operationId": "updateSandbox",
        "tags": [
          "Sandboxes"
        ],
        "summary": "Change a sandbox's settings",
        "description": "A merge, done whole or not at all: everything that can be refused is looked at before anything is changed, and when a change of `volumes` is refused after it began (a volume that a process is using does not come off), the volumes are put back as they were and no setting has changed. A change of settings wakes nothing; a change of `volumes` wakes the sandbox. A change of `egress` holds at once for a sandbox that runs. `Idempotency-Key` is not looked at.\n\nTakes `write`.",
        "security": [
          {
            "apiKey": [
              "write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "force",
            "in": "query",
            "description": "With `volumes`: take a volume away even while processes in the sandbox use it.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SandboxUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The sandbox as it is now.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Sandbox"
                }
              }
            }
          },
          "400": {
            "description": "The request is not one this call takes. `invalid_json`: the body is not a JSON object; `unknown_field`: the body has a field this call does not take; `unknown_parameter`: the query has a parameter this call does not take; `invalid_field`: a field or query parameter has the wrong type or is out of range; `invalid_name`: `name` is not a valid name; `invalid_env`: `env` has a name or a value an environment cannot hold; `invalid_metadata`: `metadata` is over its limits; `expires_in_the_past`: `expires_at` is not in the future; `invalid_egress`: `egress` is not a set of rules, or the rules it would leave are not: `param` names the part that is wrong; `invalid_path`: a volume's `path` is not absolute or is `/`.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter",
              "invalid_field",
              "invalid_name",
              "invalid_env",
              "invalid_metadata",
              "expires_in_the_past",
              "invalid_egress",
              "invalid_path",
              "invalid_inbound",
              "invalid_proxy"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "description": "The credential may not do this. `needs_api_key`: a sandbox token cannot do this. `spend_limit_reached`: the sandbox is frozen because a spend limit was reached; it sleeps, with its data, until the limit is raised. `secret_host_not_allowed`: the rules as they would be after the change send a secret by `egress.inject` to a host it may not go to (`param` says which entry; `allowed_hosts` beside `error` says where it may go). `account_share_reached`: a read-only view of a Drive's version among `volumes` (slice D7), where the Drive's account holds its share of this node's data drive already.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "needs_api_key",
              "spend_limit_reached",
              "secret_host_not_allowed",
              "account_share_reached"
            ]
          },
          "404": {
            "description": "Something the request names does not exist. `sandbox_not_found`: no such sandbox; `volume_not_found`: a volume named in `volumes` does not exist; `secret_not_found`: a secret named in `egress.inject` does not exist.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "sandbox_not_found",
              "volume_not_found",
              "secret_not_found"
            ]
          },
          "409": {
            "description": "The state of something stands in the way. `name_taken`: another sandbox has the name; `volume_in_use`: a volume in `volumes` that is not shared is another sandbox's; `duplicate_volume`: a volume is listed twice in `volumes`; `mount_path_taken`: two volumes in `volumes` have one `path`; `too_many_volumes`: `volumes` has more that are not shared than the sandbox's machine has spare drives (eight; two for one made from a template or a snapshot of before there were eight); `mount_in_use`: a process in the sandbox is using a volume that is to be detached; `force=true` detaches it anyway; `detached_mount_in_use`: every spare drive a volume to be attached could go behind is still held by a file system a process in the sandbox has open, of a volume detached from it with `force=true`; `sandbox_failed`: the sandbox is `failed`. `volume_restoring`: a volume it names is being made from a backup, or could not be. `busy`: a volume that is to be detached (with `force=true` or without) is a Drive on which the sandbox's guest has a read held up (its blocks do not come: the message names the Drive and says for how long): it stays attached, and can be detached once that read ends. `checkpoints_unavailable` (`param` `checkpoint`): `checkpoint` turns checkpoints on or changes their interval, and this node takes none (it does not copy its disks to an object store, or its memory is not in layers). `static_instance`: the sandbox is the instance the node keeps of a static spec (`GET /internal/static`): its settings are its spec's.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "name_taken",
              "volume_in_use",
              "duplicate_volume",
              "mount_path_taken",
              "too_many_volumes",
              "mount_in_use",
              "detached_mount_in_use",
              "sandbox_failed",
              "volume_restoring",
              "busy",
              "no_ipv6",
              "checkpoints_unavailable",
              "static_instance"
            ]
          },
          "503": {
            "description": "Not available. `not_served_here`: `egress` rules were given, and this node cannot put rules in force (it has no `nft`); or `unavailable`: a volume in `volumes` could not be attached in time, and the same call is made again (`Retry-After`): the guest's mount of it did not finish within 60 s (a Drive whose blocks do not come: its object store away, or its fetcher out of reach), or it is still being taken off the machine after such an attach, or it is a Drive that comes block by block and nothing answers for its blocks yet.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "not_served_here",
              "unavailable"
            ]
          },
          "default": {
            "$ref": "#/components/responses/Failure"
          }
        }
      },
      "parameters": [
        {
          "$ref": "#/components/parameters/SandboxRef"
        },
        {
          "$ref": "#/components/parameters/RequestId"
        }
      ]
    },
    "/v1/sandboxes/{id}/sleep": {
      "post": {
        "operationId": "sleepSandbox",
        "tags": [
          "Sandboxes"
        ],
        "summary": "Put a sandbox to sleep now",
        "description": "Saves its memory and ends its machine, whatever is going on in it. A call that is waiting for a command in it does not fail: it waits on, and gets the command's result if something wakes the sandbox before its time is up. If nothing does, a `run` answers 202 and a `GET …/exec/{exec_id}?wait_seconds=` answers 200, each with the command as it stood when the sandbox went to sleep, and neither wakes it. A sandbox that sleeps already, or is `failed`, is returned as it is; one that is `starting` is waited for, and so is one whose create has not answered yet (its volumes are being attached, or the start command its template holds back is waited for: `TemplateBuildRequest.start_after_volumes`), three minutes at the most. There is no call to wake a sandbox: whatever needs it wakes it. Takes no body, or `{}`.\n\nTakes `write`.",
        "security": [
          {
            "apiKey": [
              "write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/NodeIdempotencyKey"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/NoFields"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The sandbox, `sleeping`.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/Idempotent-Replayed"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Sandbox"
                }
              }
            }
          },
          "400": {
            "description": "The request is not one this call takes. `invalid_json`: the body is not a JSON object; `unknown_field`: the body has a field this call does not take; `unknown_parameter`: the query has a parameter this call does not take; `invalid_idempotency_key`: `Idempotency-Key` is empty or over 255 characters.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/Idempotent-Replayed"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter",
              "invalid_idempotency_key"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "description": "The credential may not do this. `needs_api_key`: a sandbox token cannot do this.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/Idempotent-Replayed"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "needs_api_key",
              "spend_limit_reached"
            ]
          },
          "404": {
            "description": "Something the request names does not exist. `sandbox_not_found`: no such sandbox.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/Idempotent-Replayed"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "sandbox_not_found"
            ]
          },
          "409": {
            "description": "The state of something stands in the way. `sandbox_failed`: the sandbox is `failed`; `idempotency_key_reused`: the key was used with another body; `idempotency_in_progress`: the first request with the key is still running; `busy`: a volume is still behind one of the sandbox's spare drives after an attach that did not get through (its guest's reads of it are held up, a Drive whose blocks do not come), and is taken off it once they end; or the sandbox's guest has a read held up on a Drive it has (its blocks do not come: the message names the Drive and says for how long), until that read ends; or its create has still not answered after three minutes: the same call is made again in a moment. `static_instance`: the sandbox is the instance the node keeps of a static spec (`GET /internal/static`), which never sleeps but as its node stops.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "sandbox_failed",
              "idempotency_key_reused",
              "idempotency_in_progress",
              "busy",
              "static_instance"
            ]
          },
          "default": {
            "$ref": "#/components/responses/Failure"
          }
        }
      },
      "parameters": [
        {
          "$ref": "#/components/parameters/SandboxRef"
        },
        {
          "$ref": "#/components/parameters/RequestId"
        }
      ]
    },
    "/v1/sandboxes/{id}/restart": {
      "post": {
        "operationId": "restartSandbox",
        "tags": [
          "Sandboxes"
        ],
        "summary": "Restart a sandbox from its disk",
        "description": "Stops the machine and boots it again from its disk: what ran is gone (commands included), what was written stays; id, name, links and volumes stay; `booted_at` changes. The way out of `failed` for a sandbox that has a disk. A call that is waiting for a command in it answers 409 `command_lost`. Takes no body, or `{}`.\n\nTakes `write`.",
        "security": [
          {
            "apiKey": [
              "write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/NodeIdempotencyKey"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/NoFields"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The sandbox, `running`.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/Idempotent-Replayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Sandbox"
                }
              }
            }
          },
          "400": {
            "description": "The request is not one this call takes. `invalid_json`: the body is not a JSON object; `unknown_field`: the body has a field this call does not take; `unknown_parameter`: the query has a parameter this call does not take; `invalid_idempotency_key`: `Idempotency-Key` is empty or over 255 characters.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/Idempotent-Replayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter",
              "invalid_idempotency_key"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "description": "The credential may not do this. `needs_api_key`: a sandbox token cannot do this. `spend_limit_reached`: the sandbox is frozen because a spend limit was reached; it sleeps, with its data, until the limit is raised.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/Idempotent-Replayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "needs_api_key",
              "spend_limit_reached"
            ]
          },
          "404": {
            "description": "Something the request names does not exist. `sandbox_not_found`: no such sandbox.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/Idempotent-Replayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "sandbox_not_found"
            ]
          },
          "409": {
            "description": "The state of something stands in the way. `busy`: the sandbox is still `starting`, or its create has not answered yet (its volumes are being attached, or the start command its template holds back is waited for), or its guest has a read held up on a Drive it has (its blocks do not come: the message names the Drive and says for how long), until that read ends, and its machine runs on as it was; `sandbox_failed`: the sandbox failed before it had a disk: there is nothing to boot; or its template holds its start command back (`TemplateBuildRequest.start_after_volumes`) and the command did not come up after the boot, its volumes mounted: the message says what it printed last, and the sandbox has only its disk, which the next call that needs it boots again; `idempotency_key_reused`: the key was used with another body; `idempotency_in_progress`: the first request with the key is still running.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "busy",
              "sandbox_failed",
              "idempotency_key_reused",
              "idempotency_in_progress"
            ]
          },
          "default": {
            "$ref": "#/components/responses/Failure"
          }
        }
      },
      "parameters": [
        {
          "$ref": "#/components/parameters/SandboxRef"
        },
        {
          "$ref": "#/components/parameters/RequestId"
        }
      ]
    },
    "/v1/sandboxes/{id}/fork": {
      "post": {
        "operationId": "forkSandbox",
        "tags": [
          "Sandboxes"
        ],
        "summary": "Copy a running sandbox",
        "description": "Makes `count` sandboxes that go on from this instant, memory, processes and disk, each with its own id, hostname and network. Either all are made or none is left. The copies have the original's settings; shared volumes come with them, a volume that is not shared stands in the way. Wakes the sandbox.\n\nOn a node that copies its disks to an object store, a copy's disk goes there whole before anything written to it afterwards counts as copied. That takes what the original's disk holds over what the node's link carries for such copies: measured on a development machine against a store 33 ms away, 1 GiB in 2 s and 4 GiB in 8 s on a node where nothing else is written, 4 GiB in 23 s beside ten disks written at full speed (a quarter of the node's connections are for such copies while other disks have something to send). Such copies take turns by owner (`X-Sandbox-Owner`; on a node that belongs to no control plane every sandbox has the same owner), and inside an owner the one that was made first goes first: eight copies of 2 GiB each from one call are there one after the other, the first after 5 s and the last after 35 s, and another owner's copy of 256 MiB made a second later is there after 1.3 s and does not wait for them. Until its disk is there a copy is not in the store at all: a host that is lost takes the copy with it. And for that long the copy's writes to its disk wait, from 0.9 s after the first one; reads, and everything that does not write to the disk, go on.\n\nTakes `write`.",
        "security": [
          {
            "apiKey": [
              "write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/NodeIdempotencyKey"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ForkRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The copies, in the envelope of a list.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/Idempotent-Replayed"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ForkResult"
                }
              }
            }
          },
          "400": {
            "description": "The request is not one this call takes. `invalid_json`: the body is not a JSON object; `unknown_field`: the body has a field this call does not take; `unknown_parameter`: the query has a parameter this call does not take; `invalid_field`: a field or query parameter has the wrong type or is out of range; `invalid_idempotency_key`: `Idempotency-Key` is empty or over 255 characters.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/Idempotent-Replayed"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter",
              "invalid_field",
              "invalid_idempotency_key"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "description": "The credential may not do this. `needs_api_key`: a sandbox token cannot do this. `spend_limit_reached`: the sandbox is frozen because a spend limit was reached; it sleeps, with its data, until the limit is raised. `account_share_reached`: its account has not the room on this node's data drive for the copies (`drive.accounts` of `GET /internal/node`): the snapshot they are made from, or the memory of each and a gigabyte to write. Nothing was made.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/Idempotent-Replayed"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "needs_api_key",
              "spend_limit_reached",
              "account_share_reached"
            ]
          },
          "404": {
            "description": "Something the request names does not exist. `sandbox_not_found`: no such sandbox.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/Idempotent-Replayed"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "sandbox_not_found"
            ]
          },
          "409": {
            "description": "The state of something stands in the way. `exclusive_volume_attached`: the sandbox has a volume that is not shared; `sandbox_failed`: the sandbox is `failed`; `idempotency_key_reused`: the key was used with another body; `idempotency_in_progress`: the first request with the key is still running; `busy`: a volume is still behind one of the sandbox's spare drives after an attach that did not get through (its guest's reads of it are held up, a Drive whose blocks do not come), and is taken off it once they end; or the sandbox's guest has a read held up on a Drive it has (its blocks do not come: the message names the Drive and says for how long), until that read ends: the same call is made again in a moment. `static_instance`: the sandbox is the instance the node keeps of a static spec (`GET /internal/static`): a copy would be a second of what runs in it.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "exclusive_volume_attached",
              "sandbox_failed",
              "idempotency_key_reused",
              "idempotency_in_progress",
              "busy",
              "static_instance"
            ]
          },
          "default": {
            "$ref": "#/components/responses/Failure"
          }
        }
      },
      "parameters": [
        {
          "$ref": "#/components/parameters/SandboxRef"
        },
        {
          "$ref": "#/components/parameters/RequestId"
        }
      ]
    },
    "/v1/sandboxes/{id}/snapshots": {
      "post": {
        "operationId": "createSnapshot",
        "tags": [
          "Snapshots"
        ],
        "summary": "Take a snapshot of a sandbox",
        "description": "Saves memory, device state and disk as they are at one instant; the sandbox pauses for as long as that takes and runs on. Returns when the snapshot is complete. Wakes the sandbox. Two requests for the same name at once: one gets the name, the other 409 `name_taken`.\n\nTakes `write`.",
        "security": [
          {
            "apiKey": [
              "write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/NodeIdempotencyKey"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SnapshotCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The snapshot.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Location": {
                "$ref": "#/components/headers/Location"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/Idempotent-Replayed"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Snapshot"
                }
              }
            }
          },
          "400": {
            "description": "The request is not one this call takes. `invalid_json`: the body is not a JSON object; `unknown_field`: the body has a field this call does not take; `unknown_parameter`: the query has a parameter this call does not take; `invalid_field`: a field or query parameter has the wrong type or is out of range; `invalid_name`: `name` is not a valid name; `expires_in_the_past`: `expires_at` is not in the future; `invalid_idempotency_key`: `Idempotency-Key` is empty or over 255 characters.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/Idempotent-Replayed"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter",
              "invalid_field",
              "invalid_name",
              "expires_in_the_past",
              "invalid_idempotency_key"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "description": "The credential may not do this. `needs_api_key`: a sandbox token cannot do this. `spend_limit_reached`: the sandbox is frozen because a spend limit was reached; it sleeps, with its data, until the limit is raised. `account_share_reached`: its account has not the room on this node's data drive for the snapshot: the sandbox's memory, its disk and Drives as they hold now (`drive.accounts` of `GET /internal/node`). Nothing was made.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/Idempotent-Replayed"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "needs_api_key",
              "spend_limit_reached",
              "account_share_reached"
            ]
          },
          "404": {
            "description": "Something the request names does not exist. `sandbox_not_found`: no such sandbox.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/Idempotent-Replayed"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "sandbox_not_found"
            ]
          },
          "409": {
            "description": "The state of something stands in the way. `name_taken`: a snapshot has the name and `replace` is not set; `exclusive_volume_attached`: the sandbox has a volume that is not shared; `sandbox_failed`: the sandbox is `failed`; `idempotency_key_reused`: the key was used with another body; `idempotency_in_progress`: the first request with the key is still running; `busy`: a volume is still behind one of the sandbox's spare drives after an attach that did not get through (its guest's reads of it are held up, a Drive whose blocks do not come), and is taken off it once they end; or the sandbox's guest has a read held up on a Drive it has (its blocks do not come: the message names the Drive and says for how long), until that read ends: the same call is made again in a moment.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "name_taken",
              "exclusive_volume_attached",
              "sandbox_failed",
              "idempotency_key_reused",
              "idempotency_in_progress",
              "busy"
            ]
          },
          "default": {
            "$ref": "#/components/responses/Failure"
          }
        }
      },
      "parameters": [
        {
          "$ref": "#/components/parameters/SandboxRef"
        },
        {
          "$ref": "#/components/parameters/RequestId"
        }
      ]
    },
    "/v1/sandboxes/{id}/restore": {
      "post": {
        "operationId": "restoreSandbox",
        "tags": [
          "Snapshots"
        ],
        "summary": "Roll a sandbox back to a snapshot",
        "description": "In place: same id, name and links, the memory, processes and disk of the snapshot. Any snapshot will do, not only one of this sandbox. What the sandbox was just before is kept as a new snapshot, returned in `before`. The commands that ran in it are gone; the snapshot's are there instead. Wakes the sandbox.\n\nOn a node that copies its disks to an object store, the disk as it is after the call goes there whole, as a fork's does (`POST /v1/sandboxes/{id}/fork` says how long that takes). Until it is there the sandbox's writes to its disk wait, from 0.9 s after the first one, and a host that is lost brings the sandbox back with the disk it had before the call.\n\nTakes `write`.",
        "security": [
          {
            "apiKey": [
              "write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/NodeIdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RestoreRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The sandbox as it is now, and the snapshot of what it was.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/Idempotent-Replayed"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RestoreResult"
                }
              }
            }
          },
          "400": {
            "description": "The request is not one this call takes. `invalid_json`: the body is not a JSON object; `unknown_field`: the body has a field this call does not take; `unknown_parameter`: the query has a parameter this call does not take; `invalid_field`: a field or query parameter has the wrong type or is out of range; `missing_field`: `snapshot` is missing; `invalid_idempotency_key`: `Idempotency-Key` is empty or over 255 characters.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/Idempotent-Replayed"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter",
              "invalid_field",
              "missing_field",
              "invalid_idempotency_key"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "description": "The credential may not do this. `needs_api_key`: a sandbox token cannot do this. `spend_limit_reached`: the sandbox is frozen because a spend limit was reached; it sleeps, with its data, until the limit is raised. `account_share_reached`: its account has not the room on this node's data drive for the snapshot a roll-back first takes of what the sandbox is (`drive.accounts` of `GET /internal/node`). The sandbox is as it was.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/Idempotent-Replayed"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "needs_api_key",
              "spend_limit_reached",
              "account_share_reached"
            ]
          },
          "404": {
            "description": "Something the request names does not exist. `sandbox_not_found`: no such sandbox; `snapshot_not_found`: no such snapshot.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/Idempotent-Replayed"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "sandbox_not_found",
              "snapshot_not_found"
            ]
          },
          "409": {
            "description": "The state of something stands in the way. `exclusive_volume_attached`: the sandbox has a volume that is not shared; `snapshot_lost`: the snapshot's `status` is `lost`; `sandbox_failed`: the sandbox is `failed`; `idempotency_key_reused`: the key was used with another body; `idempotency_in_progress`: the first request with the key is still running; `busy`: a volume is still behind one of the sandbox's spare drives after an attach that did not get through (its guest's reads of it are held up, a Drive whose blocks do not come), and is taken off it once they end; or the sandbox's guest has a read held up on a Drive it has (its blocks do not come: the message names the Drive and says for how long), until that read ends: the same call is made again in a moment. `static_instance`: the sandbox is the instance the node keeps of a static spec (`GET /internal/static`).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "exclusive_volume_attached",
              "snapshot_lost",
              "sandbox_failed",
              "idempotency_key_reused",
              "idempotency_in_progress",
              "busy",
              "static_instance"
            ]
          },
          "default": {
            "$ref": "#/components/responses/Failure"
          }
        }
      },
      "parameters": [
        {
          "$ref": "#/components/parameters/SandboxRef"
        },
        {
          "$ref": "#/components/parameters/RequestId"
        }
      ]
    },
    "/v1/sandboxes/{id}/tokens": {
      "post": {
        "operationId": "createSandboxToken",
        "tags": [
          "Sandboxes"
        ],
        "summary": "Mint a token for one sandbox",
        "description": "A short-lived credential for a browser or a program that should not hold the project's key. It is signed with a key of the sandbox's own, and is good on whichever node holds the sandbox: a move to another node takes the key along. It is not stored and cannot be listed: `POST /v1/sandboxes/{id}/tokens/revoke` revokes this one alone (by `token`, or by `token_id`: the `id` this call answers with), or every token of the sandbox signed so far. Wakes nothing.\n\nTakes `write`.",
        "security": [
          {
            "apiKey": [
              "write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/NodeIdempotencyKey"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TokenRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The token.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/Idempotent-Replayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SandboxToken"
                }
              }
            }
          },
          "400": {
            "description": "The request is not one this call takes. `invalid_json`: the body is not a JSON object; `unknown_field`: the body has a field this call does not take; `unknown_parameter`: the query has a parameter this call does not take; `invalid_field`: a field or query parameter has the wrong type or is out of range; `invalid_idempotency_key`: `Idempotency-Key` is empty or over 255 characters.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/Idempotent-Replayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter",
              "invalid_field",
              "invalid_idempotency_key"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "description": "The credential may not do this. `needs_api_key`: a sandbox token cannot do this.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/Idempotent-Replayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "needs_api_key"
            ]
          },
          "404": {
            "description": "Something the request names does not exist. `sandbox_not_found`: no such sandbox.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/Idempotent-Replayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "sandbox_not_found"
            ]
          },
          "409": {
            "description": "The state of something stands in the way. `idempotency_key_reused`: the key was used with another body; `idempotency_in_progress`: the first request with the key is still running.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "idempotency_key_reused",
              "idempotency_in_progress"
            ]
          },
          "default": {
            "$ref": "#/components/responses/Failure"
          }
        }
      },
      "parameters": [
        {
          "$ref": "#/components/parameters/SandboxRef"
        },
        {
          "$ref": "#/components/parameters/RequestId"
        }
      ]
    },
    "/v1/sandboxes/{id}/tokens/revoke": {
      "post": {
        "operationId": "revokeSandboxTokens",
        "tags": [
          "Sandboxes"
        ],
        "summary": "Revoke every token of a sandbox, or one",
        "description": "With no body (or `{}`): every token signed for this sandbox before this call (those of `POST /v1/sandboxes/{id}/tokens`, in a preview's cookie or link, held by an SSH session) is refused from now on (401 `invalid_token`); tokens signed after it work. Tokens carry the generation of the sandbox's tokens they were signed at, and this moves the generation on by one; it is in the sandbox's record before the call answers, and stays after a restart of the daemon, a sleep and a move to another node.\n\nWith `token` (the token itself, which has to be one of this sandbox's) or `token_id` (its `id`, as `POST /v1/sandboxes/{id}/tokens` answered it): that token alone is refused from now on, and every other token of the sandbox works as before; the generation does not move. Nothing is kept of the tokens that are signed: the node keeps the id of each token revoked so until the token runs out (`expires_at`; named by its id alone, for as long as a token may live, 30 days), in the sandbox's record before the call answers, after a restart, a sleep and a move too. A sandbox keeps 1,000 such at the most that have not run out: one more is refused 409 `too_many_revoked_tokens`, and a revoke of every token does it.\n\nA sandbox token cannot do this. Wakes nothing. While the sandbox is being moved to another node the call is refused 503 `unavailable` (call again: it then reaches the node it went to); a node whose lease does not cover a keyed or stateful sandbox refuses it 503 `fenced`.\n\nTakes `write`.",
        "security": [
          {
            "apiKey": [
              "write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/NodeIdempotencyKey"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TokenRevokeRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The revocation: of every token, the generation tokens are signed at from now on; of one, its id and until when its revoke is kept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/Idempotent-Replayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TokenRevocation"
                }
              }
            }
          },
          "400": {
            "description": "The request is not one this call takes. `invalid_json`: the body is not a JSON object; `unknown_field`: the body has a field this call does not take; `unknown_parameter`: the query has a parameter this call does not take; `invalid_field`: `token` is not a token of this sandbox, `token_id` is not a token's id, or both are given; `invalid_idempotency_key`: `Idempotency-Key` is empty or over 255 characters.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/Idempotent-Replayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter",
              "invalid_field",
              "invalid_idempotency_key"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "description": "The credential may not do this. `needs_api_key`: a sandbox token cannot do this.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/Idempotent-Replayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "needs_api_key"
            ]
          },
          "404": {
            "description": "Something the request names does not exist. `sandbox_not_found`: no such sandbox.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/Idempotent-Replayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "sandbox_not_found"
            ]
          },
          "409": {
            "description": "The state of something stands in the way. `moved`: this node released the sandbox to another node, which has it now and revokes its tokens there (`to`, `epoch`); `too_many_revoked_tokens`: the sandbox keeps as many tokens revoked one by one as it may (1,000 that have not run out): revoke every token instead; `idempotency_key_reused`: the key was used with another body; `idempotency_in_progress`: the first request with the key is still running.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "moved",
              "too_many_revoked_tokens",
              "idempotency_key_reused",
              "idempotency_in_progress"
            ]
          },
          "default": {
            "$ref": "#/components/responses/Failure"
          }
        }
      },
      "parameters": [
        {
          "$ref": "#/components/parameters/SandboxRef"
        },
        {
          "$ref": "#/components/parameters/RequestId"
        }
      ]
    },
    "/v1/sandboxes/{id}/connect": {
      "get": {
        "operationId": "connectSandbox",
        "tags": [
          "Operator"
        ],
        "summary": "A connection the sandbox opens, for the SSH gateway's port forwards (WebSocket)",
        "description": "A TCP connection the guest opens, from inside the sandbox, to `host`:`port`, and a WebSocket whose binary messages are its bytes both ways: what the SSH gateway makes of each connection a client's port forward asks for (`ssh -L <port>:<the sandbox's forward socket>`, a SOCKS proxy into the sandbox). Because the guest does the connecting, a service the sandbox binds on 127.0.0.1 is reached, a connection to the internet leaves as the sandbox's own and is counted as its egress, and the sandbox's egress rules decide it as they decide any connection it makes: what they refuse is refused, and nothing the sandbox cannot reach is reached (not the node, not the host's other addresses, not other sandboxes, not private ranges). A refusal comes before the upgrade: `connect_failed` with the guest's reason. The client's text message `ForwardEof` says it sends no more: the guest half-closes the connection, and what the peer still sends comes back. The server closes with 1000 when the peer closes, and when nothing passed either way for `ssh_forwards.idle_seconds` of `GET /internal/node`. A sandbox has at most `ssh_forwards.per_sandbox` of these open at once. Wakes the sandbox, and keeps it awake while open. Opened only with a session token of the `forward` scope, which the node's SSH gateway is given for each session (`POST /internal/ssh/authorize`), and a control plane for the sessions of a gateway in front of it: a project cannot have one (`POST …/tokens` gives `forward` to the operator alone). \n\nIts messages are in [Event streams and terminals](/ru/docs/api/#event-streams-and-terminals).\n\nTakes no key of a project, or a token of the sandbox with `forward`.",
        "x-internal": true,
        "security": [
          {
            "sandboxToken": [
              "forward"
            ]
          }
        ],
        "parameters": [
          {
            "name": "host",
            "in": "query",
            "required": true,
            "description": "What to connect to, as the guest resolves it: a name, or an IPv4 or IPv6 address. 127.0.0.1 is the sandbox's own.",
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255
            }
          },
          {
            "name": "port",
            "in": "query",
            "required": true,
            "description": "The port: 1 to 65535.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 65535
            }
          },
          {
            "name": "Upgrade",
            "in": "header",
            "required": true,
            "description": "`websocket`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Sec-WebSocket-Key",
            "in": "header",
            "required": true,
            "description": "RFC 6455.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "101": {
            "description": "The connection is a WebSocket from here on.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Upgrade": {
                "description": "`websocket`.",
                "required": true,
                "schema": {
                  "type": "string",
                  "const": "websocket"
                }
              },
              "Connection": {
                "description": "`Upgrade`.",
                "required": true,
                "schema": {
                  "type": "string",
                  "const": "Upgrade"
                }
              },
              "Sec-WebSocket-Accept": {
                "description": "The answer to `Sec-WebSocket-Key` (RFC 6455).",
                "required": true,
                "schema": {
                  "type": "string"
                }
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            }
          },
          "400": {
            "description": "The request is not one this call takes. `unknown_parameter`: the query has a parameter this call does not take; `websocket_expected`: the request is not a WebSocket upgrade; `invalid_field`: `host` or `port` is missing or out of range.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter",
              "websocket_expected",
              "invalid_field"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "description": "The credential may not do this. `needs_forward`: it is not a sandbox token (a project's key, the operator's); `token_scope`: the sandbox token does not carry `forward`, which only the SSH gateway's sessions' do. `spend_limit_reached`: the sandbox is frozen because a spend limit was reached; it sleeps, with its data, until the limit is raised.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "needs_forward",
              "token_scope",
              "spend_limit_reached"
            ]
          },
          "404": {
            "description": "Something the request names does not exist. `sandbox_not_found`: no such sandbox.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "sandbox_not_found"
            ]
          },
          "409": {
            "description": "The state of something stands in the way. `sandbox_failed`: the sandbox is `failed`; `sandbox_too_old`: the sandbox was made before the node could do this.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "sandbox_failed",
              "sandbox_too_old"
            ]
          },
          "502": {
            "description": "`connect_failed`: the guest could not open the connection; the message has its reason (`does not resolve`, or ` [errno N]`: 111 nothing listens or the sandbox's rules refused it, 113 or 101 no way there, 110 no answer within 10 seconds).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "connect_failed"
            ]
          },
          "503": {
            "description": "`too_many_forwards`: the sandbox has `ssh_forwards.per_sandbox` of these open already; one more when one ends. Retryable.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "too_many_forwards"
            ]
          },
          "default": {
            "$ref": "#/components/responses/Failure"
          }
        },
        "x-websocket": {
          "client": {
            "binary": "Bytes for the peer.",
            "text": [
              {
                "$ref": "#/components/schemas/ForwardEof"
              }
            ]
          },
          "server": {
            "binary": "Bytes from the peer."
          },
          "close": 1000
        }
      },
      "parameters": [
        {
          "$ref": "#/components/parameters/SandboxRef"
        },
        {
          "$ref": "#/components/parameters/RequestId"
        }
      ]
    },
    "/v1/sandboxes/{id}/agent": {
      "get": {
        "operationId": "openSandboxAgent",
        "tags": [
          "Operator"
        ],
        "summary": "The client's SSH agent in the sandbox, for one SSH session (WebSocket)",
        "description": "What the SSH gateway asks for when a client forwards its agent (`ssh -A`): the guest makes a socket, `/run/sbx/ssh-agent.<id>/agent`, in a directory of its own that only `user` (and root) may enter, and the first message says where (`AgentListening`): the session's commands get it as `SSH_AUTH_SOCK`. Then the binary messages carry the connections programs make to it, each a type byte, the connection's number (four bytes, little endian) and bytes: from the server `n` (a program connected), `o` (what it sent), `x` (it closed); from the client `d` (the agent's answer) and `x` (the agent closed it). `AgentReset` says that every connection is gone: the sandbox went to sleep. The sandbox is woken for the socket to be made and is not kept awake by it: while it sleeps the WebSocket stays, and when it runs again the socket is the same one. Closing the WebSocket ends the socket; one whose sandbox is asleep then goes by itself a minute after the sandbox runs again. A second call with the same `id` takes the socket over from the first. Opened only with a session token of the `forward` scope, which the node's SSH gateway is given for each session (`POST /internal/ssh/authorize`), and a control plane for the sessions of a gateway in front of it: a project cannot have one (`POST …/tokens` gives `forward` to the operator alone). \n\nIts messages are in [Event streams and terminals](/ru/docs/api/#event-streams-and-terminals).\n\nTakes no key of a project, or a token of the sandbox with `forward`.",
        "x-internal": true,
        "security": [
          {
            "sandboxToken": [
              "forward"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "query",
            "required": true,
            "description": "The socket's name, of the caller's choosing: 16 to 32 lower-case hexadecimal digits. The same one takes the socket up again.",
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{16,32}$"
            }
          },
          {
            "name": "user",
            "in": "query",
            "description": "Whose the socket is, as commands take a user: a name, a uid, or uid:gid. Default: the image's user.",
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 64
            }
          },
          {
            "name": "Upgrade",
            "in": "header",
            "required": true,
            "description": "`websocket`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Sec-WebSocket-Key",
            "in": "header",
            "required": true,
            "description": "RFC 6455.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "101": {
            "description": "The connection is a WebSocket from here on.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Upgrade": {
                "description": "`websocket`.",
                "required": true,
                "schema": {
                  "type": "string",
                  "const": "websocket"
                }
              },
              "Connection": {
                "description": "`Upgrade`.",
                "required": true,
                "schema": {
                  "type": "string",
                  "const": "Upgrade"
                }
              },
              "Sec-WebSocket-Accept": {
                "description": "The answer to `Sec-WebSocket-Key` (RFC 6455).",
                "required": true,
                "schema": {
                  "type": "string"
                }
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            }
          },
          "400": {
            "description": "The request is not one this call takes. `unknown_parameter`: the query has a parameter this call does not take; `websocket_expected`: the request is not a WebSocket upgrade; `invalid_field`: `id` or `user` is missing or not one; `no_such_user`: the sandbox has no such user.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter",
              "websocket_expected",
              "invalid_field",
              "no_such_user"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "description": "The credential may not do this. `needs_forward`: it is not a sandbox token (a project's key, the operator's); `token_scope`: the sandbox token does not carry `forward`, which only the SSH gateway's sessions' do. `spend_limit_reached`: the sandbox is frozen because a spend limit was reached; it sleeps, with its data, until the limit is raised.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "needs_forward",
              "token_scope",
              "spend_limit_reached"
            ]
          },
          "404": {
            "description": "Something the request names does not exist. `sandbox_not_found`: no such sandbox.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "sandbox_not_found"
            ]
          },
          "409": {
            "description": "The state of something stands in the way. `sandbox_failed`: the sandbox is `failed`; `sandbox_too_old`: the sandbox was made before the node could do this.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "sandbox_failed",
              "sandbox_too_old"
            ]
          },
          "502": {
            "description": "`agent_failed`: the guest could not make the socket; the message has its reason (16 agents are forwarded to the sandbox at most).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "agent_failed"
            ]
          },
          "default": {
            "$ref": "#/components/responses/Failure"
          }
        },
        "x-websocket": {
          "client": {
            "binary": "`d` or `x`, the connection's number and the agent's bytes."
          },
          "server": {
            "binary": "`n`, `o` or `x`, the connection's number and a program's bytes.",
            "text": [
              {
                "$ref": "#/components/schemas/AgentListening"
              },
              {
                "$ref": "#/components/schemas/AgentReset"
              }
            ]
          },
          "close": 1000
        }
      },
      "parameters": [
        {
          "$ref": "#/components/parameters/SandboxRef"
        },
        {
          "$ref": "#/components/parameters/RequestId"
        }
      ]
    },
    "/v1/sandboxes/{id}/url-secret": {
      "post": {
        "operationId": "rotateUrlSecret",
        "tags": [
          "Sandboxes"
        ],
        "summary": "Replace the secret that signs preview links",
        "description": "Every signed link handed out so far stops working (401 `invalid_link`), and the upgraded connections (WebSockets) open now that a link alone let in are ended before the call answers; public ports and tokens are not affected, nor what they let in. Wakes nothing. Takes no body, or `{}`.\n\nTakes `write`.",
        "security": [
          {
            "apiKey": [
              "write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/NodeIdempotencyKey"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/NoFields"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The sandbox, with its new `url_secret`.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/Idempotent-Replayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Sandbox"
                }
              }
            }
          },
          "400": {
            "description": "The request is not one this call takes. `invalid_json`: the body is not a JSON object; `unknown_field`: the body has a field this call does not take; `unknown_parameter`: the query has a parameter this call does not take; `invalid_idempotency_key`: `Idempotency-Key` is empty or over 255 characters.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/Idempotent-Replayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter",
              "invalid_idempotency_key"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "description": "The credential may not do this. `needs_api_key`: a sandbox token cannot do this.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/Idempotent-Replayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "needs_api_key"
            ]
          },
          "404": {
            "description": "Something the request names does not exist. `sandbox_not_found`: no such sandbox.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/Idempotent-Replayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "sandbox_not_found"
            ]
          },
          "409": {
            "description": "The state of something stands in the way. `idempotency_key_reused`: the key was used with another body; `idempotency_in_progress`: the first request with the key is still running.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "idempotency_key_reused",
              "idempotency_in_progress"
            ]
          },
          "default": {
            "$ref": "#/components/responses/Failure"
          }
        }
      },
      "parameters": [
        {
          "$ref": "#/components/parameters/SandboxRef"
        },
        {
          "$ref": "#/components/parameters/RequestId"
        }
      ]
    },
    "/v1/sandboxes/{id}/tcp": {
      "get": {
        "operationId": "listTcpPorts",
        "tags": [
          "Sandboxes"
        ],
        "summary": "The TCP ports open at the sandbox's IPv6 address",
        "description": "The TCP ports open to the internet at the sandbox's own IPv6 address (`ipv6`), each with the sources it is open to, in the order of their numbers: the tcp part of its `inbound`. Empty for a sandbox with none open, and for one with no address of its own. Wakes nothing.\n\nTakes `read`.",
        "security": [
          {
            "apiKey": [
              "read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "The TCP ports open, with their sources.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboundPortList"
                }
              }
            }
          },
          "400": {
            "description": "The request is not one this call takes. `unknown_parameter`: the query has a parameter this call does not take.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "description": "The credential may not do this. `needs_api_key`: a sandbox token cannot do this.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "needs_api_key"
            ]
          },
          "404": {
            "description": "Something the request names does not exist. `sandbox_not_found`: no such sandbox.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "sandbox_not_found"
            ]
          },
          "default": {
            "$ref": "#/components/responses/Failure"
          }
        }
      },
      "parameters": [
        {
          "$ref": "#/components/parameters/SandboxRef"
        },
        {
          "$ref": "#/components/parameters/RequestId"
        }
      ]
    },
    "/v1/sandboxes/{id}/tcp/{port}": {
      "put": {
        "operationId": "openTcpPort",
        "tags": [
          "Sandboxes"
        ],
        "summary": "Open a TCP port at the sandbox's IPv6 address",
        "description": "Opens a TCP port at the sandbox's own IPv6 address to the sources `from` lists, and to nobody else, or gives a port that is open those sources in place of the ones it had. Inbound is closed by default: what comes to the address is let in when it belongs to a connection the node has seen, when it is a ping, or when it is to a port open to its source; the rest is refused at once (a connection with a reset). `::/0` is everybody, and has to be said. The port is open from the moment the call answers, also while the sandbox sleeps (what comes wakes it). A sandbox opens 64 ports at the most.\n\nTakes `write`.",
        "security": [
          {
            "apiKey": [
              "write"
            ]
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/InboundPortOpen"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The port as it is open now, and where it is reached.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboundPortShown"
                }
              }
            }
          },
          "400": {
            "description": "The request is not one this call takes. `invalid_json`: the body is not a JSON object; `unknown_field`: the body has a field this call does not take; `unknown_parameter`: the query has a parameter this call does not take; `invalid_port`: the port in the path is no number from 1 to 65535; `invalid_from`: `from` is missing, empty, longer than 32, or has what is no IPv6 prefix with nothing set past its length; `invalid_inbound`: the sandbox has 64 ports open already.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter",
              "invalid_port",
              "invalid_from",
              "invalid_inbound"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "description": "The credential may not do this. `needs_api_key`: a sandbox token cannot do this.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "needs_api_key"
            ]
          },
          "404": {
            "description": "Something the request names does not exist. `sandbox_not_found`: no such sandbox.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "sandbox_not_found"
            ]
          },
          "409": {
            "description": "The state of something stands in the way. `no_ipv6`: the sandbox has no IPv6 address of its own (its node has no pool, or its template's agent is from before addresses of their own), and nothing can be opened at one. `static_instance`: the sandbox is the instance the node keeps of a static spec (`GET /internal/static`): what is open at its address is its spec's.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "no_ipv6",
              "static_instance"
            ]
          },
          "default": {
            "$ref": "#/components/responses/Failure"
          }
        }
      },
      "delete": {
        "operationId": "closeTcpPort",
        "tags": [
          "Sandboxes"
        ],
        "summary": "Close a TCP port at the sandbox's IPv6 address",
        "description": "Closes a TCP port: new connections to it are refused from the moment the call answers; those it has go on. One that was not open is no error (`was_open` is false).\n\nTakes `write`.",
        "security": [
          {
            "apiKey": [
              "write"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "The port is closed; `was_open` says whether it was open.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboundPortClosed"
                }
              }
            }
          },
          "400": {
            "description": "The request is not one this call takes. `unknown_parameter`: the query has a parameter this call does not take; `invalid_port`: the port in the path is no number from 1 to 65535.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter",
              "invalid_port"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "description": "The credential may not do this. `needs_api_key`: a sandbox token cannot do this.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "needs_api_key"
            ]
          },
          "404": {
            "description": "Something the request names does not exist. `sandbox_not_found`: no such sandbox.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "sandbox_not_found"
            ]
          },
          "409": {
            "description": "The call is refused. `static_instance`: the sandbox is the instance the node keeps of a static spec (`GET /internal/static`): what is open at its address is its spec's.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "static_instance"
            ]
          },
          "default": {
            "$ref": "#/components/responses/Failure"
          }
        }
      },
      "parameters": [
        {
          "$ref": "#/components/parameters/SandboxRef"
        },
        {
          "$ref": "#/components/parameters/InboundPortNumber"
        },
        {
          "$ref": "#/components/parameters/RequestId"
        }
      ]
    },
    "/v1/sandboxes/{id}/udp": {
      "get": {
        "operationId": "listUdpPorts",
        "tags": [
          "Sandboxes"
        ],
        "summary": "The UDP ports open at the sandbox's IPv6 address",
        "description": "The UDP ports open to the internet at the sandbox's own IPv6 address (`ipv6`), each with the sources it is open to, in the order of their numbers: the udp part of its `inbound`. Empty for a sandbox with none open, and for one with no address of its own. Wakes nothing.\n\nTakes `read`.",
        "security": [
          {
            "apiKey": [
              "read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "The UDP ports open, with their sources.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboundPortList"
                }
              }
            }
          },
          "400": {
            "description": "The request is not one this call takes. `unknown_parameter`: the query has a parameter this call does not take.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "description": "The credential may not do this. `needs_api_key`: a sandbox token cannot do this.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "needs_api_key"
            ]
          },
          "404": {
            "description": "Something the request names does not exist. `sandbox_not_found`: no such sandbox.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "sandbox_not_found"
            ]
          },
          "default": {
            "$ref": "#/components/responses/Failure"
          }
        }
      },
      "parameters": [
        {
          "$ref": "#/components/parameters/SandboxRef"
        },
        {
          "$ref": "#/components/parameters/RequestId"
        }
      ]
    },
    "/v1/sandboxes/{id}/udp/{port}": {
      "put": {
        "operationId": "openUdpPort",
        "tags": [
          "Sandboxes"
        ],
        "summary": "Open a UDP port at the sandbox's IPv6 address",
        "description": "Opens a UDP port at the sandbox's own IPv6 address to the sources `from` lists, and to nobody else, or gives a port that is open those sources in place of the ones it had. Inbound is closed by default: what comes to the address is let in when it belongs to a connection the node has seen, when it is a ping, or when it is to a port open to its source; the rest is refused at once (a connection with a reset). `::/0` is everybody, and has to be said. The port is open from the moment the call answers, also while the sandbox sleeps (what comes wakes it). A sandbox opens 64 ports at the most.\n\nTakes `write`.",
        "security": [
          {
            "apiKey": [
              "write"
            ]
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/InboundPortOpen"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The port as it is open now, and where it is reached.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboundPortShown"
                }
              }
            }
          },
          "400": {
            "description": "The request is not one this call takes. `invalid_json`: the body is not a JSON object; `unknown_field`: the body has a field this call does not take; `unknown_parameter`: the query has a parameter this call does not take; `invalid_port`: the port in the path is no number from 1 to 65535; `invalid_from`: `from` is missing, empty, longer than 32, or has what is no IPv6 prefix with nothing set past its length; `invalid_inbound`: the sandbox has 64 ports open already.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter",
              "invalid_port",
              "invalid_from",
              "invalid_inbound"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "description": "The credential may not do this. `needs_api_key`: a sandbox token cannot do this.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "needs_api_key"
            ]
          },
          "404": {
            "description": "Something the request names does not exist. `sandbox_not_found`: no such sandbox.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "sandbox_not_found"
            ]
          },
          "409": {
            "description": "The state of something stands in the way. `no_ipv6`: the sandbox has no IPv6 address of its own (its node has no pool, or its template's agent is from before addresses of their own), and nothing can be opened at one. `static_instance`: the sandbox is the instance the node keeps of a static spec (`GET /internal/static`): what is open at its address is its spec's.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "no_ipv6",
              "static_instance"
            ]
          },
          "default": {
            "$ref": "#/components/responses/Failure"
          }
        }
      },
      "delete": {
        "operationId": "closeUdpPort",
        "tags": [
          "Sandboxes"
        ],
        "summary": "Close a UDP port at the sandbox's IPv6 address",
        "description": "Closes a UDP port: new connections to it are refused from the moment the call answers; those it has go on. One that was not open is no error (`was_open` is false).\n\nTakes `write`.",
        "security": [
          {
            "apiKey": [
              "write"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "The port is closed; `was_open` says whether it was open.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboundPortClosed"
                }
              }
            }
          },
          "400": {
            "description": "The request is not one this call takes. `unknown_parameter`: the query has a parameter this call does not take; `invalid_port`: the port in the path is no number from 1 to 65535.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter",
              "invalid_port"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "description": "The credential may not do this. `needs_api_key`: a sandbox token cannot do this.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "needs_api_key"
            ]
          },
          "404": {
            "description": "Something the request names does not exist. `sandbox_not_found`: no such sandbox.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "sandbox_not_found"
            ]
          },
          "409": {
            "description": "The call is refused. `static_instance`: the sandbox is the instance the node keeps of a static spec (`GET /internal/static`): what is open at its address is its spec's.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "static_instance"
            ]
          },
          "default": {
            "$ref": "#/components/responses/Failure"
          }
        }
      },
      "parameters": [
        {
          "$ref": "#/components/parameters/SandboxRef"
        },
        {
          "$ref": "#/components/parameters/InboundPortNumber"
        },
        {
          "$ref": "#/components/parameters/RequestId"
        }
      ]
    },
    "/v1/sandboxes/{id}/url": {
      "get": {
        "operationId": "getPortUrl",
        "tags": [
          "Sandboxes"
        ],
        "summary": "The preview URL of a port",
        "description": "Whoever holds the project's key can compute the same from `url_pattern` and `url_secret`; this call is for the holder of a token. Wakes nothing; nothing needs to listen on the port.\n\nTakes `url`, or a token of the sandbox with `url`.",
        "security": [
          {
            "apiKey": [
              "url"
            ]
          },
          {
            "sandboxToken": [
              "url"
            ]
          },
          {
            "sandboxTokenInQuery": [
              "url"
            ]
          }
        ],
        "parameters": [
          {
            "name": "port",
            "in": "query",
            "description": "The port in the sandbox.",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 65535
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The URL.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PortUrl"
                }
              }
            }
          },
          "400": {
            "description": "The request is not one this call takes. `unknown_parameter`: the query has a parameter this call does not take; `invalid_field`: a field or query parameter has the wrong type or is out of range.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter",
              "invalid_field"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "description": "The credential may not do this. `token_scope`: the sandbox token lacks the scope.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "token_scope"
            ]
          },
          "404": {
            "description": "Something the request names does not exist. `sandbox_not_found`: no such sandbox.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "sandbox_not_found"
            ]
          },
          "503": {
            "description": "Not available. `no_preview_domain`: the node has no preview domain.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "no_preview_domain"
            ]
          },
          "default": {
            "$ref": "#/components/responses/Failure"
          }
        }
      },
      "parameters": [
        {
          "$ref": "#/components/parameters/SandboxRef"
        },
        {
          "$ref": "#/components/parameters/RequestId"
        }
      ]
    },
    "/v1/sandboxes/{id}/run": {
      "post": {
        "operationId": "runCommand",
        "tags": [
          "Commands"
        ],
        "summary": "Run a command and wait for its result",
        "description": "Starts the command and answers when it has ended (200) or when `wait_seconds` are up (202 with the command's address). A command that has ended by the time the wait is up is read to its end and answered 200: a 202 is always a command that is still running. Waiting keeps the sandbox from going to sleep by itself; if it is put to sleep while this call waits, the call waits on, and when `wait_seconds` are up it answers 202 from what the node noted of the command as the sandbox went to sleep, without waking it. The command belongs to the sandbox, not to this request: it runs on when the caller goes away. With an `Idempotency-Key`, the same key and body again finds the command the first request started and answers from it, with `Idempotent-Replayed: true`; the same key with another body is 409 `idempotency_key_reused`.\n\nAs a stream (`text/event-stream`): its events are `start`, `stdout`, `stderr`, `gap`, `exit` and `error`: [Event streams and terminals](/ru/docs/api/#event-streams-and-terminals).\n\nTakes `exec`, or a token of the sandbox with `exec`.",
        "security": [
          {
            "apiKey": [
              "exec"
            ]
          },
          {
            "sandboxToken": [
              "exec"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/NodeIdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RunRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The command has ended: its result as JSON; or, as an event stream, the command from its first byte to its end.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Location": {
                "description": "Only on the event stream: the command's address.",
                "schema": {
                  "type": "string",
                  "pattern": "^/v1/"
                }
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/Idempotent-Replayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExecResult"
                }
              },
              "text/event-stream": {
                "schema": {
                  "type": "string",
                  "description": "When `stream` is true, or `Accept` asks for `text/event-stream`. A stream waits for the command however long it takes: `wait_seconds` does not apply. Its events are `start`, `stdout`, `stderr`, `gap`, `exit` and `error`: [Event streams and terminals](/ru/docs/api/#event-streams-and-terminals)."
                },
                "x-events": [
                  "start",
                  "stdout",
                  "stderr",
                  "gap",
                  "exit",
                  "error"
                ]
              }
            }
          },
          "202": {
            "description": "Still running when `wait_seconds` were up: not a result. `status` is `running`, and `stdout` and `stderr` hold what the command had written by then. Ask the address in `Location` (with `wait_seconds`) for the result.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Location": {
                "$ref": "#/components/headers/Location"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/Idempotent-Replayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExecResult"
                }
              }
            }
          },
          "400": {
            "description": "The request is not one this call takes. `invalid_json`: the body is not a JSON object; `unknown_field`: the body has a field this call does not take; `unknown_parameter`: the query has a parameter this call does not take; `invalid_field`: a field or query parameter has the wrong type or is out of range; `missing_command`: no `command`; `invalid_env`: `env` has a name or a value an environment cannot hold; `no_such_user`: `user` is not a user of the sandbox; `terminal_needs_exec`: `tty` was given; `invalid_idempotency_key`: `Idempotency-Key` is empty or over 255 characters.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter",
              "invalid_field",
              "missing_command",
              "invalid_env",
              "no_such_user",
              "terminal_needs_exec",
              "invalid_idempotency_key"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "description": "The credential may not do this. `token_scope`: the sandbox token lacks the scope. `spend_limit_reached`: the sandbox is frozen because a spend limit was reached; it sleeps, with its data, until the limit is raised.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "token_scope",
              "spend_limit_reached"
            ]
          },
          "404": {
            "description": "Something the request names does not exist. `sandbox_not_found`: no such sandbox.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "sandbox_not_found"
            ]
          },
          "409": {
            "description": "The state of something stands in the way. `command_lost`: the command went away while the call waited for it; `sandbox_moved`: the sandbox was moved to another node while the call waited, and the command goes on there (`to`, `epoch`, `offset`, and `exec_id`, the command's id): wait for it there through the control plane with `GET …/exec/{exec_id}?wait_seconds=`, or make the same call again with the same Idempotency-Key (without one it starts the command a second time); `sandbox_failed`: the sandbox is `failed`; `idempotency_key_reused`: the key was used with another body.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "command_lost",
              "sandbox_moved",
              "sandbox_failed",
              "idempotency_key_reused"
            ]
          },
          "default": {
            "$ref": "#/components/responses/Failure"
          }
        }
      },
      "parameters": [
        {
          "$ref": "#/components/parameters/SandboxRef"
        },
        {
          "$ref": "#/components/parameters/RequestId"
        }
      ]
    },
    "/v1/sandboxes/{id}/exec": {
      "get": {
        "operationId": "listCommands",
        "tags": [
          "Commands"
        ],
        "summary": "List a sandbox's commands",
        "description": "Newest first, the newest 1000 at most. Wakes nothing: while the sandbox sleeps the list is what the node last noted. A restart from disk empties it; after a restore it holds the commands the snapshot had, as a sandbox made from a snapshot or by a fork holds the commands that were running when it was taken.\n\nTakes `exec`, or a token of the sandbox with `exec`.",
        "security": [
          {
            "apiKey": [
              "exec"
            ]
          },
          {
            "sandboxToken": [
              "exec"
            ]
          },
          {
            "sandboxTokenInQuery": [
              "exec"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/NodeLimit"
          },
          {
            "$ref": "#/components/parameters/NodeCursor"
          }
        ],
        "responses": {
          "200": {
            "description": "One page.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExecList"
                }
              }
            }
          },
          "400": {
            "description": "The request is not one this call takes. `unknown_parameter`: the query has a parameter this call does not take; `invalid_field`: a field or query parameter has the wrong type or is out of range; `invalid_cursor`: `cursor` is not base64.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter",
              "invalid_field",
              "invalid_cursor"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "description": "The credential may not do this. `token_scope`: the sandbox token lacks the scope.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "token_scope"
            ]
          },
          "404": {
            "description": "Something the request names does not exist. `sandbox_not_found`: no such sandbox.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "sandbox_not_found"
            ]
          },
          "default": {
            "$ref": "#/components/responses/Failure"
          }
        }
      },
      "post": {
        "operationId": "startCommand",
        "tags": [
          "Commands"
        ],
        "summary": "Start a command and return at once",
        "description": "For servers, long jobs and terminals. The command runs on whether or not anyone watches; its output is kept (the last 16 MiB) and can be read from any position at any time. With an `Idempotency-Key`, the same key and body again answers with the command the first request started, and `Idempotent-Replayed: true`; the same key with another body is 409 `idempotency_key_reused`.\n\nTakes `exec`, or a token of the sandbox with `exec`.",
        "security": [
          {
            "apiKey": [
              "exec"
            ]
          },
          {
            "sandboxToken": [
              "exec"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/NodeIdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ExecRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The command's handle.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Location": {
                "$ref": "#/components/headers/Location"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/Idempotent-Replayed"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Exec"
                }
              }
            }
          },
          "400": {
            "description": "The request is not one this call takes. `invalid_json`: the body is not a JSON object; `unknown_field`: the body has a field this call does not take; `unknown_parameter`: the query has a parameter this call does not take; `invalid_field`: a field or query parameter has the wrong type or is out of range; `missing_command`: no `command`; `invalid_env`: `env` has a name or a value an environment cannot hold; `no_such_user`: `user` is not a user of the sandbox; `exec_does_not_wait`: `wait_seconds` or `stream` was given; `invalid_idempotency_key`: `Idempotency-Key` is empty or over 255 characters.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter",
              "invalid_field",
              "missing_command",
              "invalid_env",
              "no_such_user",
              "exec_does_not_wait",
              "invalid_idempotency_key"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "description": "The credential may not do this. `token_scope`: the sandbox token lacks the scope. `spend_limit_reached`: the sandbox is frozen because a spend limit was reached; it sleeps, with its data, until the limit is raised.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "token_scope",
              "spend_limit_reached"
            ]
          },
          "404": {
            "description": "Something the request names does not exist. `sandbox_not_found`: no such sandbox.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "sandbox_not_found"
            ]
          },
          "409": {
            "description": "The state of something stands in the way. `sandbox_failed`: the sandbox is `failed`; `idempotency_key_reused`: the key was used with another body.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "sandbox_failed",
              "idempotency_key_reused"
            ]
          },
          "default": {
            "$ref": "#/components/responses/Failure"
          }
        }
      },
      "parameters": [
        {
          "$ref": "#/components/parameters/SandboxRef"
        },
        {
          "$ref": "#/components/parameters/RequestId"
        }
      ]
    },
    "/v1/sandboxes/{id}/exec/{exec_id}": {
      "get": {
        "operationId": "getCommand",
        "tags": [
          "Commands"
        ],
        "summary": "How a command stands, with the end of its output",
        "description": "With `wait_seconds`, the answer comes when the command has ended or the time is up, whichever is first; when the time is up the answer is 200 with `status` `running`, and the caller asks again. Waiting keeps the sandbox from going to sleep by itself; if it is put to sleep while this call waits, the call waits on, and answers when its time is up with the command as it stood when the sandbox went to sleep, without waking it; if the sandbox is restarted from its disk meanwhile, the answer is 409 `command_lost`. Wakes a sandbox that sleeps, to read the command's output; not for an id the sandbox does not have, which is 404 from what the node noted.\n\nTakes `exec`, or a token of the sandbox with `exec`.",
        "security": [
          {
            "apiKey": [
              "exec"
            ]
          },
          {
            "sandboxToken": [
              "exec"
            ]
          },
          {
            "sandboxTokenInQuery": [
              "exec"
            ]
          }
        ],
        "parameters": [
          {
            "name": "wait_seconds",
            "in": "query",
            "description": "Wait this long for the command to end.",
            "schema": {
              "type": "number",
              "minimum": 0,
              "maximum": 3600,
              "default": 0
            }
          },
          {
            "name": "output",
            "in": "query",
            "description": "`false`: leave out `stdout`, `stderr`, `truncated` and `encoding`.",
            "schema": {
              "type": "boolean",
              "default": true
            }
          },
          {
            "name": "encoding",
            "in": "query",
            "description": "How `stdout` and `stderr` are written.",
            "schema": {
              "type": "string",
              "enum": [
                "utf8",
                "base64"
              ],
              "default": "utf8"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The command; without the output fields when `output=false`.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "anyOf": [
                    {
                      "$ref": "#/components/schemas/ExecResult"
                    },
                    {
                      "$ref": "#/components/schemas/Exec"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "The request is not one this call takes. `unknown_parameter`: the query has a parameter this call does not take; `invalid_field`: a field or query parameter has the wrong type or is out of range.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter",
              "invalid_field"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "description": "The credential may not do this. `token_scope`: the sandbox token lacks the scope. `spend_limit_reached`: the sandbox is frozen because a spend limit was reached; it sleeps, with its data, until the limit is raised.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "token_scope",
              "spend_limit_reached"
            ]
          },
          "404": {
            "description": "Something the request names does not exist. `sandbox_not_found`: no such sandbox; `exec_not_found`: no such command in the sandbox.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "sandbox_not_found",
              "exec_not_found"
            ]
          },
          "409": {
            "description": "The state of something stands in the way. `sandbox_failed`: the sandbox is `failed`; `command_lost`: the sandbox was restarted from its disk while the call waited; `sandbox_moved`: it was moved to another node while the call waited, and the command goes on there (`to`, `epoch`, `offset`): the same call through the control plane asks it there.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "sandbox_failed",
              "command_lost",
              "sandbox_moved"
            ]
          },
          "default": {
            "$ref": "#/components/responses/Failure"
          }
        }
      },
      "parameters": [
        {
          "$ref": "#/components/parameters/SandboxRef"
        },
        {
          "$ref": "#/components/parameters/ExecRef"
        },
        {
          "$ref": "#/components/parameters/RequestId"
        }
      ]
    },
    "/v1/sandboxes/{id}/exec/{exec_id}/output": {
      "get": {
        "operationId": "streamCommandOutput",
        "tags": [
          "Commands"
        ],
        "summary": "Follow a command's output",
        "description": "The output as events, from any position, to the command's end. Wakes the sandbox once, to get what there is (not for an id the sandbox does not have). After that an open stream keeps nothing awake, and goes on by itself when the sandbox next runs (a sandbox moved to another node meanwhile ends it with the `error` event `sandbox_moved`, after which the same stream through the control plane, with `Last-Event-ID`, goes on there); with `wait=true` the reader is somebody waiting for the command, and the sandbox stays awake for as long as the stream is open.\n\nAs a stream (`text/event-stream`): its events are `stdout`, `stderr`, `gap`, `exit`, `end` and `error`: [Event streams and terminals](/ru/docs/api/#event-streams-and-terminals).\n\nTakes `exec`, or a token of the sandbox with `exec`.",
        "security": [
          {
            "apiKey": [
              "exec"
            ]
          },
          {
            "sandboxToken": [
              "exec"
            ]
          },
          {
            "sandboxTokenInQuery": [
              "exec"
            ]
          }
        ],
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "description": "The position to begin at. A position that is no longer kept gives a `gap` event first; one beyond the end of the output is 400 `invalid_field`.",
            "schema": {
              "type": "integer",
              "format": "int64",
              "minimum": 0,
              "default": 0
            }
          },
          {
            "name": "Last-Event-ID",
            "in": "header",
            "description": "The same as `from`, and it wins over `from`: what an `EventSource` sends when it reconnects.",
            "schema": {
              "type": "integer",
              "format": "int64",
              "minimum": 0
            }
          },
          {
            "name": "follow",
            "in": "query",
            "description": "`false`: send what there is now and end, with `exit` if the command has ended and `end` if not.",
            "schema": {
              "type": "boolean",
              "default": true
            }
          },
          {
            "name": "encoding",
            "in": "query",
            "description": "`base64`: events carry `base64` instead of `text`.",
            "schema": {
              "type": "string",
              "enum": [
                "utf8",
                "base64"
              ],
              "default": "utf8"
            }
          },
          {
            "name": "wait",
            "in": "query",
            "description": "`true`: the reader is waiting for the command, as a call that waits for its result is. The sandbox is then not put to sleep by itself while the stream is open. For whoever picks a stream up again after a lost connection and still wants the command's end.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "wait_for_reader",
            "in": "query",
            "description": "`true`: the command waits for this reader, as for a `runCommand` with `wait_for_reader`: it gets no more than 2 MiB of output ahead of what this stream has sent, and nothing is let go of unread. One reader at a time is waited for, the last that asked; when it leaves, the command waits 10 seconds for the next. For the reader that started the command so and picks its stream up again after a lost connection (the SSH gateway, whose sessions outlive a restart of the node's daemon): without it the command runs on as one nobody waits for, keeping the last 16 MiB of its output.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The stream.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "text/event-stream": {
                "schema": {
                  "type": "string",
                  "description": "Its events are `stdout`, `stderr`, `gap`, `exit`, `end` and `error`: [Event streams and terminals](/ru/docs/api/#event-streams-and-terminals)."
                },
                "x-events": [
                  "stdout",
                  "stderr",
                  "gap",
                  "exit",
                  "end",
                  "error"
                ]
              }
            }
          },
          "400": {
            "description": "The request is not one this call takes. `unknown_parameter`: the query has a parameter this call does not take; `invalid_field`: a field or query parameter has the wrong type or is out of range.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter",
              "invalid_field"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "description": "The credential may not do this. `token_scope`: the sandbox token lacks the scope. `spend_limit_reached`: the sandbox is frozen because a spend limit was reached; it sleeps, with its data, until the limit is raised.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "token_scope",
              "spend_limit_reached"
            ]
          },
          "404": {
            "description": "Something the request names does not exist. `sandbox_not_found`: no such sandbox; `exec_not_found`: no such command in the sandbox.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "sandbox_not_found",
              "exec_not_found"
            ]
          },
          "409": {
            "description": "The state of something stands in the way. `sandbox_failed`: the sandbox is `failed`.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "sandbox_failed"
            ]
          },
          "default": {
            "$ref": "#/components/responses/Failure"
          }
        }
      },
      "parameters": [
        {
          "$ref": "#/components/parameters/SandboxRef"
        },
        {
          "$ref": "#/components/parameters/ExecRef"
        },
        {
          "$ref": "#/components/parameters/RequestId"
        }
      ]
    },
    "/v1/sandboxes/{id}/exec/{exec_id}/stdin": {
      "post": {
        "operationId": "writeCommandStdin",
        "tags": [
          "Commands"
        ],
        "summary": "Send input to a command",
        "description": "The body is the bytes themselves, unless its `Content-Type` begins with `application/json`: then it is `StdinRequest`. The command must have been started with `stdin`, `stdin_open` or `tty`. On a terminal, `eof` types end-of-input (Ctrl-D) instead of closing anything. The body is 64 MiB at the most (413 `body_too_large` beyond): a longer input goes in several calls.\n\nTakes `exec`, or a token of the sandbox with `exec`.",
        "security": [
          {
            "apiKey": [
              "exec"
            ]
          },
          {
            "sandboxToken": [
              "exec"
            ]
          }
        ],
        "parameters": [
          {
            "name": "eof",
            "in": "query",
            "description": "For a body of bytes: close the input after them. With a JSON body, `eof` is a field.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "$ref": "#/components/parameters/NodeIdempotencyKey"
          }
        ],
        "requestBody": {
          "description": "JSON, or the bytes themselves.",
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/StdinRequest"
              }
            },
            "application/octet-stream": {}
          }
        },
        "responses": {
          "204": {
            "description": "The command has the bytes.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/Idempotent-Replayed"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            }
          },
          "400": {
            "description": "The request is not one this call takes. `invalid_json`: the body is not a JSON object; `unknown_field`: the body has a field this call does not take; `unknown_parameter`: the query has a parameter this call does not take; `invalid_field`: a field or query parameter has the wrong type or is out of range; `invalid_idempotency_key`: `Idempotency-Key` is empty or over 255 characters.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/Idempotent-Replayed"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter",
              "invalid_field",
              "invalid_idempotency_key"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "description": "The credential may not do this. `token_scope`: the sandbox token lacks the scope. `spend_limit_reached`: the sandbox is frozen because a spend limit was reached; it sleeps, with its data, until the limit is raised.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/Idempotent-Replayed"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "token_scope",
              "spend_limit_reached"
            ]
          },
          "404": {
            "description": "Something the request names does not exist. `sandbox_not_found`: no such sandbox; `exec_not_found`: no such command in the sandbox.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/Idempotent-Replayed"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "sandbox_not_found",
              "exec_not_found"
            ]
          },
          "409": {
            "description": "The state of something stands in the way. `stdin_closed`: the command's input is closed; `sandbox_failed`: the sandbox is `failed`; `idempotency_key_reused`: the key was used with another body; `idempotency_in_progress`: the first request with the key is still running.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "stdin_closed",
              "sandbox_failed",
              "idempotency_key_reused",
              "idempotency_in_progress"
            ]
          },
          "default": {
            "$ref": "#/components/responses/Failure"
          }
        }
      },
      "parameters": [
        {
          "$ref": "#/components/parameters/SandboxRef"
        },
        {
          "$ref": "#/components/parameters/ExecRef"
        },
        {
          "$ref": "#/components/parameters/RequestId"
        }
      ]
    },
    "/v1/sandboxes/{id}/exec/{exec_id}/signal": {
      "post": {
        "operationId": "signalCommand",
        "tags": [
          "Commands"
        ],
        "summary": "Send a command a signal",
        "description": "The signal goes to the command and everything it started (its process group). A command that has ended answers 204 too: nothing is sent.\n\nTakes `exec`, or a token of the sandbox with `exec`.",
        "security": [
          {
            "apiKey": [
              "exec"
            ]
          },
          {
            "sandboxToken": [
              "exec"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/NodeIdempotencyKey"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SignalRequest"
              }
            }
          }
        },
        "responses": {
          "204": {
            "description": "Sent, or the command had ended.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/Idempotent-Replayed"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            }
          },
          "400": {
            "description": "The request is not one this call takes. `invalid_json`: the body is not a JSON object; `unknown_field`: the body has a field this call does not take; `unknown_parameter`: the query has a parameter this call does not take; `invalid_field`: a field or query parameter has the wrong type or is out of range; `unknown_signal`: a name or a number that is no signal; `invalid_idempotency_key`: `Idempotency-Key` is empty or over 255 characters.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/Idempotent-Replayed"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter",
              "invalid_field",
              "unknown_signal",
              "invalid_idempotency_key"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "description": "The credential may not do this. `token_scope`: the sandbox token lacks the scope. `spend_limit_reached`: the sandbox is frozen because a spend limit was reached; it sleeps, with its data, until the limit is raised.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/Idempotent-Replayed"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "token_scope",
              "spend_limit_reached"
            ]
          },
          "404": {
            "description": "Something the request names does not exist. `sandbox_not_found`: no such sandbox; `exec_not_found`: no such command in the sandbox.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/Idempotent-Replayed"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "sandbox_not_found",
              "exec_not_found"
            ]
          },
          "409": {
            "description": "The state of something stands in the way. `sandbox_failed`: the sandbox is `failed`; `idempotency_key_reused`: the key was used with another body; `idempotency_in_progress`: the first request with the key is still running.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "sandbox_failed",
              "idempotency_key_reused",
              "idempotency_in_progress"
            ]
          },
          "default": {
            "$ref": "#/components/responses/Failure"
          }
        }
      },
      "parameters": [
        {
          "$ref": "#/components/parameters/SandboxRef"
        },
        {
          "$ref": "#/components/parameters/ExecRef"
        },
        {
          "$ref": "#/components/parameters/RequestId"
        }
      ]
    },
    "/v1/sandboxes/{id}/exec/{exec_id}/tty": {
      "get": {
        "operationId": "openTerminal",
        "tags": [
          "Commands"
        ],
        "summary": "Join a command's terminal (WebSocket)",
        "description": "A WebSocket to a command started with `tty`. OpenAPI cannot describe the messages; `x-websocket` lists them. Binary messages are bytes: what is typed one way, what the terminal shows the other. Text messages are JSON: the client sends `TerminalResize` and `TerminalSignal`; the server sends `TerminalExit` when the command ends, or `TerminalError` when it was lost, and then closes with code 1000, and `TerminalGap` before what it shows next where the client fell behind by more than the sandbox keeps of the output (what it shows carries no position, and a client that counts it, to join again with `from`, goes on from there). A sandbox moved to another node meanwhile ends it with `TerminalError` `sandbox_moved` (where it went, and `offset`, the position shown up to) and the close code 1012 with the reason `sandbox_moved`: the command goes on there, and the terminal is joined again through the control plane with `from` set to `offset`. The server pings after 20 seconds of silence. Leaving does not end the command: join again, and the last 64 KiB of what the terminal showed come first (or everything from `from`). An open terminal keeps nothing awake; the next key wakes a sandbox that sleeps. A browser, which cannot set headers on a WebSocket, passes a sandbox token with scope `tty` as `sandbox_token`. A command that was not started with `tty` can be joined as well: its output arrives as binary messages.\n\nIts messages are in [Event streams and terminals](/ru/docs/api/#event-streams-and-terminals).\n\nTakes `tty`, or a token of the sandbox with `tty`.",
        "security": [
          {
            "apiKey": [
              "tty"
            ]
          },
          {
            "sandboxToken": [
              "tty"
            ]
          },
          {
            "sandboxTokenInQuery": [
              "tty"
            ]
          }
        ],
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "description": "The output position to begin at. Default: 64 KiB before the end.",
            "schema": {
              "type": "integer",
              "format": "int64",
              "minimum": 0
            }
          },
          {
            "name": "Upgrade",
            "in": "header",
            "required": true,
            "description": "`websocket`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Sec-WebSocket-Key",
            "in": "header",
            "required": true,
            "description": "RFC 6455.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "101": {
            "description": "The connection is a WebSocket from here on.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Upgrade": {
                "description": "`websocket`.",
                "required": true,
                "schema": {
                  "type": "string",
                  "const": "websocket"
                }
              },
              "Connection": {
                "description": "`Upgrade`.",
                "required": true,
                "schema": {
                  "type": "string",
                  "const": "Upgrade"
                }
              },
              "Sec-WebSocket-Accept": {
                "description": "The answer to `Sec-WebSocket-Key` (RFC 6455).",
                "required": true,
                "schema": {
                  "type": "string"
                }
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            }
          },
          "400": {
            "description": "The request is not one this call takes. `unknown_parameter`: the query has a parameter this call does not take; `websocket_expected`: the request is not a WebSocket upgrade; `invalid_field`: a field or query parameter has the wrong type or is out of range.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter",
              "websocket_expected",
              "invalid_field"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "description": "The credential may not do this. `token_scope`: the sandbox token lacks the scope. `spend_limit_reached`: the sandbox is frozen because a spend limit was reached; it sleeps, with its data, until the limit is raised.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "token_scope",
              "spend_limit_reached"
            ]
          },
          "404": {
            "description": "Something the request names does not exist. `sandbox_not_found`: no such sandbox; `exec_not_found`: no such command in the sandbox.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "sandbox_not_found",
              "exec_not_found"
            ]
          },
          "409": {
            "description": "The state of something stands in the way. `sandbox_failed`: the sandbox is `failed`.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "sandbox_failed"
            ]
          },
          "default": {
            "$ref": "#/components/responses/Failure"
          }
        },
        "x-websocket": {
          "client": {
            "binary": "Bytes for the command's input, as typed.",
            "text": [
              {
                "$ref": "#/components/schemas/TerminalResize"
              },
              {
                "$ref": "#/components/schemas/TerminalSignal"
              }
            ]
          },
          "server": {
            "binary": "Bytes the terminal shows.",
            "text": [
              {
                "$ref": "#/components/schemas/TerminalExit"
              },
              {
                "$ref": "#/components/schemas/TerminalError"
              },
              {
                "$ref": "#/components/schemas/TerminalGap"
              }
            ]
          },
          "close": 1000
        }
      },
      "parameters": [
        {
          "$ref": "#/components/parameters/SandboxRef"
        },
        {
          "$ref": "#/components/parameters/ExecRef"
        },
        {
          "$ref": "#/components/parameters/RequestId"
        }
      ]
    },
    "/v1/sandboxes/{id}/files": {
      "get": {
        "operationId": "readFile",
        "tags": [
          "Files"
        ],
        "summary": "Read a file",
        "description": "The file's bytes, or a part of them, of any length in one call. A file (or the part asked for) of up to 1 MiB is answered as it is; a longer one passes from the sandbox to the caller as it comes, a piece at a time, so that the daemon holds a bounded piece of it and never the whole: its length is said first (`Content-Length`: what the file holds when it is looked at, from `offset`, as far as `length`), and the connection is kept for the next request. A file that becomes shorter while it is read ends the answer short of that length, and the connection with it (409 `file_changed` where that is seen before anything was sent): read it again. Only a regular file is read: a pipe, a socket or a device is 409 `not_a_regular_file`. Paths are absolute. The operation is done as the user commands run as, or as `user`: what that user may not do is refused with 403, and what is created is that user's.\n\nTakes `files`, or a token of the sandbox with `files`.",
        "security": [
          {
            "apiKey": [
              "files"
            ]
          },
          {
            "sandboxToken": [
              "files"
            ]
          },
          {
            "sandboxTokenInQuery": [
              "files"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/FilePath"
          },
          {
            "name": "offset",
            "in": "query",
            "description": "Begin at this byte.",
            "schema": {
              "type": "integer",
              "format": "int64",
              "minimum": 0,
              "default": 0
            }
          },
          {
            "name": "length",
            "in": "query",
            "description": "At most this many bytes. Default: to the end of the file, however long.",
            "schema": {
              "type": "integer",
              "format": "int64",
              "minimum": 0
            }
          },
          {
            "$ref": "#/components/parameters/FileUser"
          }
        ],
        "responses": {
          "200": {
            "description": "The bytes, always as `application/octet-stream`. Empty when `offset` is at or past the end.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/octet-stream": {}
            }
          },
          "400": {
            "description": "The request is not one this call takes. `missing_path`: no path; `invalid_path`: a path that is not absolute, or one the file system does not take; `name_too_long`: a name in the path is longer than the file system takes (255 bytes); `invalid_field`: a field or query parameter has the wrong type or is out of range; `unknown_parameter`: the query has a parameter this call does not take; `no_such_user`: `user` is not a user of the sandbox.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "missing_path",
              "invalid_path",
              "name_too_long",
              "invalid_field",
              "unknown_parameter",
              "no_such_user"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "description": "The credential may not do this. `token_scope`: the sandbox token lacks the scope; `permission_denied`: the user the operation was done as may not. `spend_limit_reached`: the sandbox is frozen because a spend limit was reached; it sleeps, with its data, until the limit is raised.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "token_scope",
              "permission_denied",
              "spend_limit_reached"
            ]
          },
          "404": {
            "description": "Something the request names does not exist. `sandbox_not_found`: no such sandbox; `file_not_found`: nothing at the path.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "sandbox_not_found",
              "file_not_found"
            ]
          },
          "409": {
            "description": "The state of something stands in the way. `is_a_directory`: the path is a directory; `not_a_directory`: something on the way is not a directory; `symlink_loop`: the symbolic links on the way lead in a circle; `not_a_regular_file`: the path leads to a pipe, a socket or a device; `file_changed`: a file longer than 1 MiB became shorter between the start of its read and the look at its length (try again); `sandbox_failed`: the sandbox is `failed`.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "is_a_directory",
              "not_a_directory",
              "symlink_loop",
              "not_a_regular_file",
              "file_changed",
              "sandbox_failed"
            ]
          },
          "503": {
            "description": "Not now, and nothing was done. `body_memory_full`: the node holds as much of the bodies of calls as it takes at once, and no room came within 5 seconds; try again in a moment.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "body_memory_full"
            ]
          },
          "default": {
            "$ref": "#/components/responses/Failure"
          }
        }
      },
      "put": {
        "operationId": "writeFile",
        "tags": [
          "Files"
        ],
        "summary": "Write a file",
        "description": "The body becomes the file's whole content, put in place at once: a reader sees the old content or the new, never half of each. The body passes on to the sandbox as it comes, a piece at a time: it has no limit of its own (the sandbox's disk is its limit), and the daemon holds a bounded piece of it, never the whole. A body that ends before its length (the caller went away) puts nothing in place. With `offset` the body is instead written into the file from that position on, in place, and the rest of the file stays as it is: that is how a file is written a piece at a time, and how a part of a file is changed; what came of a body that ended short is written. Directories above it that are missing are made. `Idempotency-Key` is not looked at. Paths are absolute. The operation is done as the user commands run as, or as `user`: what that user may not do is refused with 403, and what is created is that user's.\n\nTakes `files`, or a token of the sandbox with `files`.",
        "security": [
          {
            "apiKey": [
              "files"
            ]
          },
          {
            "sandboxToken": [
              "files"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/FilePath"
          },
          {
            "name": "mode",
            "in": "query",
            "description": "Permission bits as octal digits.",
            "schema": {
              "type": "string",
              "pattern": "^[0-7]{1,4}$",
              "default": "0644"
            }
          },
          {
            "name": "offset",
            "in": "query",
            "description": "Write the body into the file from this byte on and leave what else the file holds. A file that is not there is made (with `mode`; a file that is there keeps its own); a position past the file's end leaves zeros between. Left out: the body takes the place of the file's whole content.",
            "schema": {
              "type": "integer",
              "format": "int64",
              "minimum": 0
            }
          },
          {
            "$ref": "#/components/parameters/FileUser"
          }
        ],
        "requestBody": {
          "description": "The content, of any length, passed on as it comes.",
          "required": false,
          "content": {
            "application/octet-stream": {}
          }
        },
        "responses": {
          "204": {
            "description": "Written.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            }
          },
          "400": {
            "description": "The request is not one this call takes. `missing_path`: no path; `invalid_path`: a path that is not absolute, or one the file system does not take; `name_too_long`: a name in the path is longer than the file system takes (255 bytes); `invalid_field`: a field or query parameter has the wrong type or is out of range; `unknown_parameter`: the query has a parameter this call does not take; `no_such_user`: `user` is not a user of the sandbox; `malformed_request`: the content ended before its length, or its chunks are not chunks.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "missing_path",
              "invalid_path",
              "name_too_long",
              "invalid_field",
              "unknown_parameter",
              "no_such_user",
              "malformed_request"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "description": "The credential may not do this. `token_scope`: the sandbox token lacks the scope; `permission_denied`: the user the operation was done as may not: also a new file in a directory that user cannot write, and a path under `/proc` or `/sys`. `spend_limit_reached`: the sandbox is frozen because a spend limit was reached; it sleeps, with its data, until the limit is raised.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "token_scope",
              "permission_denied",
              "spend_limit_reached"
            ]
          },
          "404": {
            "description": "Something the request names does not exist. `sandbox_not_found`: no such sandbox; `file_not_found`: something on the way to the path leads nowhere: a symbolic link to nothing.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "sandbox_not_found",
              "file_not_found"
            ]
          },
          "409": {
            "description": "The state of something stands in the way. `is_a_directory`: the path is a directory; `not_a_directory`: something on the way is not a directory; `read_only`: the file system is mounted read-only; `symlink_loop`: the symbolic links on the way lead in a circle; `file_busy`: a program is running from the file, which cannot be written in place; `not_a_regular_file`: a pipe, a socket or a device is at the path; `sandbox_too_old`: `offset` was given, and the sandbox was made before the node could write at a position; `sandbox_failed`: the sandbox is `failed`.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "is_a_directory",
              "not_a_directory",
              "read_only",
              "symlink_loop",
              "file_busy",
              "not_a_regular_file",
              "sandbox_too_old",
              "sandbox_failed"
            ]
          },
          "413": {
            "description": "No room. `disk_full`: the disk is full.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "disk_full"
            ]
          },
          "503": {
            "description": "Not now, and nothing was done. `body_memory_full`: the node holds as much of the bodies of calls as it takes at once, and no room came within 5 seconds; try again in a moment.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "body_memory_full"
            ]
          },
          "default": {
            "$ref": "#/components/responses/Failure"
          }
        }
      },
      "delete": {
        "operationId": "deleteFile",
        "tags": [
          "Files"
        ],
        "summary": "Delete a file or a directory",
        "description": "An empty directory is deleted like a file; one with something in it needs `recursive=true`. `/` is refused. Paths are absolute. The operation is done as the user commands run as, or as `user`: what that user may not do is refused with 403, and what is created is that user's.\n\nTakes `files`, or a token of the sandbox with `files`.",
        "security": [
          {
            "apiKey": [
              "files"
            ]
          },
          {
            "sandboxToken": [
              "files"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/FilePath"
          },
          {
            "name": "recursive",
            "in": "query",
            "description": "Delete a directory with everything in it. Only the literal `true` counts.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "$ref": "#/components/parameters/FileUser"
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            }
          },
          "400": {
            "description": "The request is not one this call takes. `missing_path`: no path; `invalid_path`: a path that is not absolute, or `/`; `name_too_long`: a name in the path is longer than the file system takes (255 bytes); `invalid_field`: a field or query parameter has the wrong type or is out of range; `unknown_parameter`: the query has a parameter this call does not take; `no_such_user`: `user` is not a user of the sandbox.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "missing_path",
              "invalid_path",
              "name_too_long",
              "invalid_field",
              "unknown_parameter",
              "no_such_user"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "description": "The credential may not do this. `token_scope`: the sandbox token lacks the scope; `permission_denied`: the user the operation was done as may not. `spend_limit_reached`: the sandbox is frozen because a spend limit was reached; it sleeps, with its data, until the limit is raised.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "token_scope",
              "permission_denied",
              "spend_limit_reached"
            ]
          },
          "404": {
            "description": "Something the request names does not exist. `sandbox_not_found`: no such sandbox; `file_not_found`: nothing at the path.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "sandbox_not_found",
              "file_not_found"
            ]
          },
          "409": {
            "description": "The state of something stands in the way. `directory_not_empty`: the directory has something in it; `not_a_directory`: something on the way is not a directory; `read_only`: the file system is mounted read-only; `file_busy`: a file system is mounted at the path; `symlink_loop`: the symbolic links on the way lead in a circle; `sandbox_failed`: the sandbox is `failed`.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "directory_not_empty",
              "not_a_directory",
              "read_only",
              "file_busy",
              "symlink_loop",
              "sandbox_failed"
            ]
          },
          "default": {
            "$ref": "#/components/responses/Failure"
          }
        }
      },
      "patch": {
        "operationId": "setFileAttributes",
        "tags": [
          "Files"
        ],
        "summary": "Set a file's length, mode or time of modification",
        "description": "Sets what is given and leaves the rest as it is. `size_bytes` cuts a file to that length, or extends it with zeros; `mode` sets the permission bits of a file or a directory; `modified_at` its time of last modification. A symbolic link is followed. An empty body does nothing, and wakes nothing. Paths are absolute. The operation is done as the user commands run as, or as `user`: what that user may not do is refused with 403.\n\nTakes `files`, or a token of the sandbox with `files`.",
        "security": [
          {
            "apiKey": [
              "files"
            ]
          },
          {
            "sandboxToken": [
              "files"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/FilePath"
          },
          {
            "$ref": "#/components/parameters/FileUser"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FileAttributes"
              }
            }
          }
        },
        "responses": {
          "204": {
            "description": "Set.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            }
          },
          "400": {
            "description": "The request is not one this call takes. `invalid_json`: the body is not a JSON object; `unknown_field`: the body has a field this call does not take; `missing_path`: no path; `invalid_path`: a path that is not absolute, or one whose length cannot be set (a pipe, a device); `name_too_long`: a name in the path is longer than the file system takes (255 bytes); `invalid_field`: a field or query parameter has the wrong type or is out of range; `unknown_parameter`: the query has a parameter this call does not take; `no_such_user`: `user` is not a user of the sandbox.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "missing_path",
              "invalid_path",
              "name_too_long",
              "invalid_field",
              "unknown_parameter",
              "no_such_user"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "description": "The credential may not do this. `token_scope`: the sandbox token lacks the scope; `permission_denied`: the user the operation was done as may not: it is not that user's file.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "token_scope",
              "permission_denied"
            ]
          },
          "404": {
            "description": "Something the request names does not exist. `sandbox_not_found`: no such sandbox; `file_not_found`: nothing at the path.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "sandbox_not_found",
              "file_not_found"
            ]
          },
          "409": {
            "description": "The state of something stands in the way. `is_a_directory`: a directory has no length to set; `not_a_directory`: something on the way is not a directory; `read_only`: the file system is mounted read-only; `symlink_loop`: the symbolic links on the way lead in a circle; `file_busy`: a program is running from the file, whose length cannot be changed meanwhile; `sandbox_too_old`: the sandbox was made before the node could do this; `sandbox_failed`: the sandbox is `failed`.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "is_a_directory",
              "not_a_directory",
              "read_only",
              "symlink_loop",
              "file_busy",
              "sandbox_too_old",
              "sandbox_failed"
            ]
          },
          "413": {
            "description": "No room. `disk_full`: the length is more than the file system takes.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "disk_full"
            ]
          },
          "default": {
            "$ref": "#/components/responses/Failure"
          }
        }
      },
      "parameters": [
        {
          "$ref": "#/components/parameters/SandboxRef"
        },
        {
          "$ref": "#/components/parameters/RequestId"
        }
      ]
    },
    "/v1/sandboxes/{id}/files/list": {
      "get": {
        "operationId": "listDirectory",
        "tags": [
          "Files"
        ],
        "summary": "List a directory",
        "description": "The entries, sorted by name, a page at a time like every list: `limit` and `cursor`. Paths are absolute. The operation is done as the user commands run as, or as `user`: what that user may not do is refused with 403, and what is created is that user's.\n\nTakes `files`, or a token of the sandbox with `files`.",
        "security": [
          {
            "apiKey": [
              "files"
            ]
          },
          {
            "sandboxToken": [
              "files"
            ]
          },
          {
            "sandboxTokenInQuery": [
              "files"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/FilePath"
          },
          {
            "$ref": "#/components/parameters/NodeLimit"
          },
          {
            "$ref": "#/components/parameters/NodeCursor"
          },
          {
            "$ref": "#/components/parameters/FileUser"
          }
        ],
        "responses": {
          "200": {
            "description": "One page of the entries.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FileEntryList"
                }
              }
            }
          },
          "400": {
            "description": "The request is not one this call takes. `missing_path`: no path; `invalid_path`: a path that is not absolute, or one the file system does not take; `name_too_long`: a name in the path is longer than the file system takes (255 bytes); `invalid_field`: a field or query parameter has the wrong type or is out of range; `invalid_cursor`: `cursor` is not base64; `unknown_parameter`: the query has a parameter this call does not take; `no_such_user`: `user` is not a user of the sandbox.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "missing_path",
              "invalid_path",
              "name_too_long",
              "invalid_field",
              "invalid_cursor",
              "unknown_parameter",
              "no_such_user"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "description": "The credential may not do this. `token_scope`: the sandbox token lacks the scope; `permission_denied`: the user the operation was done as may not. `spend_limit_reached`: the sandbox is frozen because a spend limit was reached; it sleeps, with its data, until the limit is raised.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "token_scope",
              "permission_denied",
              "spend_limit_reached"
            ]
          },
          "404": {
            "description": "Something the request names does not exist. `sandbox_not_found`: no such sandbox; `file_not_found`: nothing at the path.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "sandbox_not_found",
              "file_not_found"
            ]
          },
          "409": {
            "description": "The state of something stands in the way. `not_a_directory`: something on the way is not a directory; `symlink_loop`: the symbolic links on the way lead in a circle; `sandbox_failed`: the sandbox is `failed`.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "not_a_directory",
              "symlink_loop",
              "sandbox_failed"
            ]
          },
          "default": {
            "$ref": "#/components/responses/Failure"
          }
        }
      },
      "parameters": [
        {
          "$ref": "#/components/parameters/SandboxRef"
        },
        {
          "$ref": "#/components/parameters/RequestId"
        }
      ]
    },
    "/v1/sandboxes/{id}/files/stat": {
      "get": {
        "operationId": "statFile",
        "tags": [
          "Files"
        ],
        "summary": "Look at one entry",
        "description": "A symbolic link is reported as itself, or with `follow=true` as what it leads to. Paths are absolute. The operation is done as the user commands run as, or as `user`: what that user may not do is refused with 403, and what is created is that user's.\n\nTakes `files`, or a token of the sandbox with `files`.",
        "security": [
          {
            "apiKey": [
              "files"
            ]
          },
          {
            "sandboxToken": [
              "files"
            ]
          },
          {
            "sandboxTokenInQuery": [
              "files"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/FilePath"
          },
          {
            "name": "follow",
            "in": "query",
            "description": "Tell of what a symbolic link leads to, not of the link: a link that leads nowhere is then 404. Only the literal `true` counts. A sandbox made before the node knew this parameter pays no attention to it.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "$ref": "#/components/parameters/FileUser"
          }
        ],
        "responses": {
          "200": {
            "description": "The entry.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FileEntry"
                }
              }
            }
          },
          "400": {
            "description": "The request is not one this call takes. `missing_path`: no path; `invalid_path`: a path that is not absolute, or one the file system does not take; `name_too_long`: a name in the path is longer than the file system takes (255 bytes); `invalid_field`: a field or query parameter has the wrong type or is out of range; `unknown_parameter`: the query has a parameter this call does not take; `no_such_user`: `user` is not a user of the sandbox.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "missing_path",
              "invalid_path",
              "name_too_long",
              "invalid_field",
              "unknown_parameter",
              "no_such_user"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "description": "The credential may not do this. `token_scope`: the sandbox token lacks the scope; `permission_denied`: the user the operation was done as may not. `spend_limit_reached`: the sandbox is frozen because a spend limit was reached; it sleeps, with its data, until the limit is raised.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "token_scope",
              "permission_denied",
              "spend_limit_reached"
            ]
          },
          "404": {
            "description": "Something the request names does not exist. `sandbox_not_found`: no such sandbox; `file_not_found`: nothing at the path.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "sandbox_not_found",
              "file_not_found"
            ]
          },
          "409": {
            "description": "The state of something stands in the way. `not_a_directory`: something on the way is not a directory; `symlink_loop`: the symbolic links on the way lead in a circle; `sandbox_failed`: the sandbox is `failed`.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "not_a_directory",
              "symlink_loop",
              "sandbox_failed"
            ]
          },
          "default": {
            "$ref": "#/components/responses/Failure"
          }
        }
      },
      "parameters": [
        {
          "$ref": "#/components/parameters/SandboxRef"
        },
        {
          "$ref": "#/components/parameters/RequestId"
        }
      ]
    },
    "/v1/sandboxes/{id}/files/mkdir": {
      "post": {
        "operationId": "makeDirectory",
        "tags": [
          "Files"
        ],
        "summary": "Make a directory",
        "description": "Makes the directory and whatever is missing above it. A directory that is there already is not an error; anything else at the path is 409 `already_exists`. Paths are absolute. The operation is done as the user commands run as, or as `user`: what that user may not do is refused with 403, and what is created is that user's.\n\nTakes `files`, or a token of the sandbox with `files`.",
        "security": [
          {
            "apiKey": [
              "files"
            ]
          },
          {
            "sandboxToken": [
              "files"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/NodeIdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MkdirRequest"
              }
            }
          }
        },
        "responses": {
          "204": {
            "description": "The path exists.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/Idempotent-Replayed"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            }
          },
          "400": {
            "description": "The request is not one this call takes. `invalid_json`: the body is not a JSON object; `unknown_field`: the body has a field this call does not take; `unknown_parameter`: the query has a parameter this call does not take; `invalid_field`: a field or query parameter has the wrong type or is out of range; `missing_path`: no path; `invalid_path`: a path that is not absolute, or one the file system does not take; `name_too_long`: a name in the path is longer than the file system takes (255 bytes); `no_such_user`: `user` is not a user of the sandbox; `invalid_idempotency_key`: `Idempotency-Key` is empty or over 255 characters.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/Idempotent-Replayed"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter",
              "invalid_field",
              "missing_path",
              "invalid_path",
              "name_too_long",
              "no_such_user",
              "invalid_idempotency_key"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "description": "The credential may not do this. `token_scope`: the sandbox token lacks the scope; `permission_denied`: the user the operation was done as may not. `spend_limit_reached`: the sandbox is frozen because a spend limit was reached; it sleeps, with its data, until the limit is raised.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/Idempotent-Replayed"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "token_scope",
              "permission_denied",
              "spend_limit_reached"
            ]
          },
          "404": {
            "description": "Something the request names does not exist. `sandbox_not_found`: no such sandbox.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/Idempotent-Replayed"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "sandbox_not_found"
            ]
          },
          "409": {
            "description": "The state of something stands in the way. `already_exists`: something that is not a directory is at the path; `not_a_directory`: something on the way is not a directory; `read_only`: the file system is mounted read-only; `symlink_loop`: the symbolic links on the way lead in a circle; `sandbox_failed`: the sandbox is `failed`; `idempotency_key_reused`: the key was used with another body; `idempotency_in_progress`: the first request with the key is still running.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "already_exists",
              "not_a_directory",
              "read_only",
              "symlink_loop",
              "sandbox_failed",
              "idempotency_key_reused",
              "idempotency_in_progress"
            ]
          },
          "413": {
            "description": "No room. `disk_full`: the disk is full.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/Idempotent-Replayed"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "disk_full"
            ]
          },
          "default": {
            "$ref": "#/components/responses/Failure"
          }
        }
      },
      "parameters": [
        {
          "$ref": "#/components/parameters/SandboxRef"
        },
        {
          "$ref": "#/components/parameters/RequestId"
        }
      ]
    },
    "/v1/sandboxes/{id}/files/move": {
      "post": {
        "operationId": "moveFile",
        "tags": [
          "Files"
        ],
        "summary": "Move or rename",
        "description": "A rename: a file at `to` is replaced. A move to another file system (onto or off a volume, into `/dev/shm`) is done by copying and then removing the original, which is not one step: a reader may see `to` while `from` is still there. A refusal says in `param` whether it is about `from` or `to`, where the sandbox's system lets that be told. Paths are absolute. The operation is done as the user commands run as, or as `user`: what that user may not do is refused with 403, and what is created is that user's.\n\nTakes `files`, or a token of the sandbox with `files`.",
        "security": [
          {
            "apiKey": [
              "files"
            ]
          },
          {
            "sandboxToken": [
              "files"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/NodeIdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MoveRequest"
              }
            }
          }
        },
        "responses": {
          "204": {
            "description": "Moved.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/Idempotent-Replayed"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            }
          },
          "400": {
            "description": "The request is not one this call takes. `invalid_json`: the body is not a JSON object; `unknown_field`: the body has a field this call does not take; `unknown_parameter`: the query has a parameter this call does not take; `invalid_field`: a field or query parameter has the wrong type or is out of range; `missing_path`: no path; `invalid_path`: a path that is not absolute, or one the file system does not take; `name_too_long`: a name in the path is longer than the file system takes (255 bytes); `moved_into_itself`: `to` is `from` itself or lies beneath it; `no_such_user`: `user` is not a user of the sandbox; `invalid_idempotency_key`: `Idempotency-Key` is empty or over 255 characters.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/Idempotent-Replayed"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter",
              "invalid_field",
              "missing_path",
              "invalid_path",
              "name_too_long",
              "moved_into_itself",
              "no_such_user",
              "invalid_idempotency_key"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "description": "The credential may not do this. `token_scope`: the sandbox token lacks the scope; `permission_denied`: the user the operation was done as may not. `spend_limit_reached`: the sandbox is frozen because a spend limit was reached; it sleeps, with its data, until the limit is raised.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/Idempotent-Replayed"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "token_scope",
              "permission_denied",
              "spend_limit_reached"
            ]
          },
          "404": {
            "description": "Something the request names does not exist. `sandbox_not_found`: no such sandbox; `file_not_found`: nothing at `from`, or the directory `to` is in does not exist; `param` says which.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/Idempotent-Replayed"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "sandbox_not_found",
              "file_not_found"
            ]
          },
          "409": {
            "description": "The state of something stands in the way. `directory_not_empty`: `to` is a directory with something in it; `is_a_directory`: the path is a directory; `not_a_directory`: something on the way is not a directory; `read_only`: the file system is mounted read-only; `symlink_loop`: the symbolic links on the way lead in a circle; `file_busy`: a file system is mounted at `from` or at `to`; `sandbox_failed`: the sandbox is `failed`; `idempotency_key_reused`: the key was used with another body; `idempotency_in_progress`: the first request with the key is still running.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "directory_not_empty",
              "is_a_directory",
              "not_a_directory",
              "read_only",
              "symlink_loop",
              "file_busy",
              "sandbox_failed",
              "idempotency_key_reused",
              "idempotency_in_progress"
            ]
          },
          "413": {
            "description": "No room. `disk_full`: the file system `to` is on has no room for the copy.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/Idempotent-Replayed"
              },
              "Server-Timing": {
                "$ref": "#/components/headers/Server-Timing"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "disk_full"
            ]
          },
          "default": {
            "$ref": "#/components/responses/Failure"
          }
        }
      },
      "parameters": [
        {
          "$ref": "#/components/parameters/SandboxRef"
        },
        {
          "$ref": "#/components/parameters/RequestId"
        }
      ]
    },
    "/v1/snapshots": {
      "get": {
        "operationId": "listSnapshots",
        "tags": [
          "Snapshots"
        ],
        "summary": "List snapshots",
        "description": "Newest first.\n\nTakes `read`.",
        "security": [
          {
            "apiKey": [
              "read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/NodeLimit"
          },
          {
            "$ref": "#/components/parameters/NodeCursor"
          },
          {
            "name": "sandbox_id",
            "in": "query",
            "description": "Only snapshots taken of this sandbox: its id or its name. A sandbox that has been deleted can only be named by its id.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "One page.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SnapshotList"
                }
              }
            }
          },
          "400": {
            "description": "The request is not one this call takes. `unknown_parameter`: the query has a parameter this call does not take; `invalid_field`: a field or query parameter has the wrong type or is out of range; `invalid_cursor`: `cursor` is not base64.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter",
              "invalid_field",
              "invalid_cursor"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "description": "The credential may not do this. `needs_api_key`: a sandbox token cannot do this.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "needs_api_key"
            ]
          },
          "default": {
            "$ref": "#/components/responses/Failure"
          }
        }
      },
      "parameters": [
        {
          "$ref": "#/components/parameters/RequestId"
        }
      ]
    },
    "/v1/snapshots/{id}": {
      "get": {
        "operationId": "getSnapshot",
        "tags": [
          "Snapshots"
        ],
        "summary": "Look at a snapshot",
        "description": "By id or by name.\n\nTakes `read`.",
        "security": [
          {
            "apiKey": [
              "read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "The snapshot.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Snapshot"
                }
              }
            }
          },
          "400": {
            "description": "The request is not one this call takes. `unknown_parameter`: the query has a parameter this call does not take.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "description": "The credential may not do this. `needs_api_key`: a sandbox token cannot do this.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "needs_api_key"
            ]
          },
          "404": {
            "description": "Something the request names does not exist. `snapshot_not_found`: no such snapshot.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "snapshot_not_found"
            ]
          },
          "default": {
            "$ref": "#/components/responses/Failure"
          }
        }
      },
      "delete": {
        "operationId": "deleteSnapshot",
        "tags": [
          "Snapshots"
        ],
        "summary": "Delete a snapshot",
        "description": "Sandboxes made from it keep running, and keep its id in their `snapshot` field.\n\nTakes `write`.",
        "security": [
          {
            "apiKey": [
              "write"
            ]
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "The request is not one this call takes. `unknown_parameter`: the query has a parameter this call does not take.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "description": "The credential may not do this. `needs_api_key`: a sandbox token cannot do this.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "needs_api_key"
            ]
          },
          "404": {
            "description": "Something the request names does not exist. `snapshot_not_found`: no such snapshot.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "snapshot_not_found"
            ]
          },
          "default": {
            "$ref": "#/components/responses/Failure"
          }
        }
      },
      "patch": {
        "operationId": "updateSnapshot",
        "tags": [
          "Snapshots"
        ],
        "summary": "Rename a snapshot or change when it expires",
        "description": "A merge: fields left out stay as they are.\n\nTakes `write`.",
        "security": [
          {
            "apiKey": [
              "write"
            ]
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SnapshotUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The snapshot as it is now.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Snapshot"
                }
              }
            }
          },
          "400": {
            "description": "The request is not one this call takes. `invalid_json`: the body is not a JSON object; `unknown_field`: the body has a field this call does not take; `unknown_parameter`: the query has a parameter this call does not take; `invalid_field`: a field or query parameter has the wrong type or is out of range; `invalid_name`: `name` is not a valid name; `expires_in_the_past`: `expires_at` is not in the future.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter",
              "invalid_field",
              "invalid_name",
              "expires_in_the_past"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "description": "The credential may not do this. `needs_api_key`: a sandbox token cannot do this.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "needs_api_key"
            ]
          },
          "404": {
            "description": "Something the request names does not exist. `snapshot_not_found`: no such snapshot.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "snapshot_not_found"
            ]
          },
          "409": {
            "description": "The state of something stands in the way. `name_taken`: another snapshot has the name.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "name_taken"
            ]
          },
          "default": {
            "$ref": "#/components/responses/Failure"
          }
        }
      },
      "parameters": [
        {
          "$ref": "#/components/parameters/SnapshotRef"
        },
        {
          "$ref": "#/components/parameters/RequestId"
        }
      ]
    },
    "/v1/secrets": {
      "get": {
        "operationId": "listSecrets",
        "tags": [
          "Secrets"
        ],
        "summary": "List secrets",
        "description": "The names, by name, with the hosts each may go to and when each was made and last changed. No values: no call returns one.\n\nTakes `read`.",
        "security": [
          {
            "apiKey": [
              "read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/NodeLimit"
          },
          {
            "$ref": "#/components/parameters/NodeCursor"
          }
        ],
        "responses": {
          "200": {
            "description": "One page.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SecretList"
                }
              }
            }
          },
          "400": {
            "description": "The request is not one this call takes. `unknown_parameter`: the query has a parameter this call does not take; `invalid_field`: a field or query parameter has the wrong type or is out of range; `invalid_cursor`: `cursor` is not base64.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter",
              "invalid_field",
              "invalid_cursor"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "description": "The credential may not do this. `needs_api_key`: a sandbox token cannot do this.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "needs_api_key"
            ]
          },
          "default": {
            "$ref": "#/components/responses/Failure"
          }
        }
      },
      "parameters": [
        {
          "$ref": "#/components/parameters/RequestId"
        }
      ]
    },
    "/v1/secrets/{name}": {
      "put": {
        "operationId": "putSecret",
        "tags": [
          "Secrets"
        ],
        "summary": "Put a secret",
        "description": "Keeps a value under a name, in place of whatever was there, with the hosts it may be sent to (`allowed_hosts`, and those it had where none are given). The node keeps it sealed (AES-256-GCM under a key of its own) and writes it into the requests that `egress.inject` rules name it for, to those hosts and no other; the next such request carries the new value. The answer is the secret without its value. A secret is its owner's, as a sandbox is: the rules of a sandbox name the secrets of that sandbox's owner and no others, and two owners may keep two values under one name.\n\nTakes `admin`.",
        "security": [
          {
            "apiKey": [
              "admin"
            ]
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SecretPut"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The secret, without its value.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Secret"
                }
              }
            }
          },
          "400": {
            "description": "The request is not one this call takes. `invalid_json`: the body is not a JSON object; `unknown_field`: the body has a field this call does not take; `unknown_parameter`: the query has a parameter this call does not take; `invalid_field`: a field or query parameter has the wrong type or is out of range, or an entry of `allowed_hosts` is no host name (an address, a port, a range; `param` says which); `invalid_name`: the name in the path is not one a secret can have; `missing_field`: no `value`; `invalid_secret`: the value is empty, over 65536 bytes, or has a line break or another control character in it.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter",
              "invalid_field",
              "invalid_name",
              "missing_field",
              "invalid_secret"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "description": "The credential may not do this. `needs_api_key`: a sandbox token cannot do this.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "needs_api_key"
            ]
          },
          "default": {
            "$ref": "#/components/responses/Failure"
          }
        }
      },
      "delete": {
        "operationId": "deleteSecret",
        "tags": [
          "Secrets"
        ],
        "summary": "Delete a secret",
        "description": "Whether or not rules still name it: a request that needs a secret that is gone is refused by the node (`secret_missing`, in `x-egress-proxy`), not sent without.\n\nTakes `admin`.",
        "security": [
          {
            "apiKey": [
              "admin"
            ]
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            }
          },
          "400": {
            "description": "The request is not one this call takes. `unknown_parameter`: the query has a parameter this call does not take.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "description": "The credential may not do this. `needs_api_key`: a sandbox token cannot do this.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "needs_api_key"
            ]
          },
          "404": {
            "description": "Something the request names does not exist. `secret_not_found`: no such secret.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "secret_not_found"
            ]
          },
          "default": {
            "$ref": "#/components/responses/Failure"
          }
        }
      },
      "patch": {
        "operationId": "updateSecret",
        "tags": [
          "Secrets"
        ],
        "summary": "Change where a secret may go",
        "description": "Gives a secret the hosts it may be sent to in place of those it had, and keeps its value: for whoever decides where a secret goes and does not hold the value. Rules that name it for a host it may no longer go to stay as they were written, and the node sends no request there with it (`secret_host_not_allowed`, in `x-egress-proxy`); what is given or changed after is held to the new hosts.\n\nTakes `admin`.",
        "security": [
          {
            "apiKey": [
              "admin"
            ]
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SecretPatch"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The secret, without its value.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Secret"
                }
              }
            }
          },
          "400": {
            "description": "The request is not one this call takes. `invalid_json`: the body is not a JSON object; `unknown_field`: the body has a field this call does not take; `unknown_parameter`: the query has a parameter this call does not take; `invalid_field`: `allowed_hosts` is no list of host names, or an entry of it is no host name (`param` says which); `invalid_name`: the name in the path is not one a secret can have; `missing_field`: no `allowed_hosts`.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter",
              "invalid_field",
              "invalid_name",
              "missing_field"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "description": "The credential may not do this. `needs_api_key`: a sandbox token cannot do this.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "needs_api_key"
            ]
          },
          "404": {
            "description": "Something the request names does not exist. `secret_not_found`: no such secret.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeErrorResponse"
                }
              }
            },
            "x-codes": [
              "secret_not_found"
            ]
          },
          "default": {
            "$ref": "#/components/responses/Failure"
          }
        }
      },
      "parameters": [
        {
          "$ref": "#/components/parameters/SecretName"
        },
        {
          "$ref": "#/components/parameters/RequestId"
        }
      ]
    },
    "/v1/auth/sign-up": {
      "post": {
        "operationId": "signUp",
        "tags": [
          "Console"
        ],
        "summary": "Sign up: a user, an account of its own, and a mail that verifies the address",
        "description": "No session, and the console's header and origin, as every call under `/v1/auth/` (`X-Requested-By: console`, `Origin`: `consoleSession` says why). With `email` and `password`: the user (the address trimmed and in lower case: one mailbox is one user however it is written), an account of its own on the free plan whose owner it is, with a first project called `default` and the address as where the account's notices go (`account_name` is what the account is called; left out, the address), and a mail to the address with a link that verifies it (`verify_email`, 24 hours): 202. An address that has a user makes nothing, and that user is mailed that somebody signed up with its address (`already_signed_up`; a user an operator locked is mailed nothing): the answer is the same, after the same work (a password is hashed either way), so that nobody learns from it whether an address has a user. Until its address is verified the user signs in and reads, and makes nothing (403 `email_unverified`). With `invitation`, the token of an invitation's link, and `password`: the user, verified (the invitation came to its address), a member of the inviting account with the role it was invited as, and no account of its own: 201; `email` may be left out, and where it is given it is the invitation's address. The password is 10 to 256 characters, and not the address.",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SignUp"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Signed up through the invitation: the user, verified, a member of the account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/User"
                }
              }
            }
          },
          "202": {
            "description": "The address is mailed: a link that verifies it, or, where it has a user, that somebody signed up with it. The same answer either way.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SignUpAccepted"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_json`, `unknown_field`, `unknown_parameter`; `missing_field`: no `email` (without `invitation`), no `password`; `invalid_field`: an `email` that is no address, a `password` of fewer than 10 or more than 256 characters or that is the address, an `account_name` that is not 1 to 100 characters with no control character among them, or that comes with `invitation`; a field that is not a string.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter",
              "missing_field",
              "invalid_field"
            ]
          },
          "403": {
            "description": "`csrf_header_missing`: no `X-Requested-By: console`; `csrf_origin_mismatch`: an `Origin` that is not the console's (`consoleSession` says which, and why); `invitation_for_another_address`: an `email` that is not the invitation's address.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "csrf_header_missing",
              "csrf_origin_mismatch",
              "invitation_for_another_address"
            ]
          },
          "404": {
            "description": "`invitation_not_found`: no invitation may be taken with the token: none such, taken, revoked, or run out.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "invitation_not_found"
            ]
          },
          "409": {
            "description": "`user_exists`: the invitation's address has a user, which signs in and accepts it there (`POST /v1/me/invitations`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "user_exists"
            ]
          },
          "429": {
            "description": "`rate_limited` (`retryable`): too many sign-ups of the address for now (3, then one every ten minutes) or from the caller's source (10, then one every six minutes; through an invitation, the source's alone), whether or not the address has a user. The try counts for nothing.",
            "headers": {
              "Retry-After": {
                "required": true,
                "schema": {
                  "type": "string",
                  "pattern": "^[0-9]+$"
                },
                "description": "The seconds until one more try is taken."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "rate_limited"
            ]
          }
        }
      }
    },
    "/v1/auth/verify": {
      "post": {
        "operationId": "verifyEmail",
        "tags": [
          "Console"
        ],
        "summary": "Verify an address, by the link of its mail",
        "description": "No session; the console's header and origin. `token`: the token of the link that verifies an address (`ev_...`), which the console's page reads from the fragment of its own address (`#/verify?token=…`, which no request carries). A link is taken once.",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/VerifyEmail"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The user, its address verified.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/User"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_json`, `unknown_field`, `unknown_parameter`; `missing_field`: no `token`, an empty one, or one of more than 200 characters; `invalid_field`: a `token` that is not a string.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter",
              "missing_field",
              "invalid_field"
            ]
          },
          "403": {
            "$ref": "#/components/responses/ConsoleCsrf"
          },
          "404": {
            "description": "`token_not_found`: the link is not one this control plane made, has been taken, or ran out (24 hours).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "token_not_found"
            ]
          }
        }
      }
    },
    "/v1/auth/providers": {
      "get": {
        "operationId": "listAuthProviders",
        "tags": [
          "Console"
        ],
        "summary": "The providers a user signs in with",
        "description": "No session. The providers the control plane has an OAuth app of, GitHub and Google among them or neither: the console shows a button for each. Without one, its routes are 404 `no_such_route`.",
        "security": [],
        "responses": {
          "200": {
            "description": "The providers, GitHub first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthProviderList"
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`: the call takes none.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter"
            ]
          }
        }
      }
    },
    "/v1/auth/oauth/{provider}": {
      "parameters": [
        {
          "name": "provider",
          "in": "path",
          "required": true,
          "description": "`github` or `google`.",
          "schema": {
            "type": "string"
          }
        }
      ],
      "post": {
        "operationId": "startOAuth",
        "tags": [
          "Console"
        ],
        "summary": "A sign-in with GitHub or Google: the provider's page to go to",
        "description": "No session; the console's header and origin. The page of the provider to send the browser to, with the sign-in's `state` and its PKCE challenge (S256), and for Google a nonce; and a cookie that binds the sign-in to this browser. The provider sends the browser back to `oauthCallback` within ten minutes. Counts as a sign-in from the caller's source (`POST /v1/auth/sign-in`).",
        "security": [],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OAuthStart"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The provider's page, and the cookie.",
            "headers": {
              "Set-Cookie": {
                "required": true,
                "schema": {
                  "type": "string",
                  "pattern": "^(__Host-console-oauth|console-oauth)=[A-Za-z0-9_-]{43}; Path=/; HttpOnly; SameSite=Lax; Max-Age=600(; Secure)?$"
                },
                "description": "The cookie that binds the sign-in to this browser (`__Host-console-oauth`, ten minutes): the provider's answer is taken only from the browser that has it. `console-oauth`, without `Secure`, on a control plane started with `--console-insecure-cookies true`."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OAuthStarted"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_json`, `unknown_field`, `unknown_parameter`; `invalid_field`: a `next` that is no page of the console's accounts (a path that begins with `/a/`, 500 characters at the most), or a field that is not a string.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter",
              "invalid_field"
            ]
          },
          "403": {
            "$ref": "#/components/responses/ConsoleCsrf"
          },
          "404": {
            "description": "`no_such_route`: the control plane has no OAuth app of the provider (`--oauth-github-client-id`, `--oauth-google-client-id`), or the path names none such.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "no_such_route"
            ]
          },
          "429": {
            "description": "`rate_limited` (`retryable`): too many sign-ins from the caller's source for now (30, then one every 10 seconds), those with a password among them.",
            "headers": {
              "Retry-After": {
                "required": true,
                "schema": {
                  "type": "string",
                  "pattern": "^[0-9]+$"
                },
                "description": "The seconds until one more try is taken."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "rate_limited"
            ]
          },
          "503": {
            "description": "`oauth_provider_unavailable` (`retryable`): Google's discovery document (its endpoints) could not be read, and none was read before.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "oauth_provider_unavailable"
            ]
          }
        }
      }
    },
    "/v1/auth/oauth/{provider}/callback": {
      "parameters": [
        {
          "name": "provider",
          "in": "path",
          "required": true,
          "description": "`github` or `google`.",
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "operationId": "oauthCallback",
        "tags": [
          "Console"
        ],
        "summary": "The provider sends the browser back",
        "description": "No session: the address the OAuth app names (`<the console's origin>/v1/auth/oauth/<provider>/callback`), which the provider sends the browser to with the code and the state, or an error. Every answer is the console's page (303): signed in (a session's cookie) and on to the sign-in's `next`, or the user's settings of a link (`#/me?linked=<provider>`); or told why not, in the fragment: `#/sign-in?oauth_error=<code>&message=<words>` (`#/me?…` of a link). The codes: `oauth_state_invalid` (no flow of this browser has the state: forged, another browser's, back already, or older than ten minutes), `oauth_denied` (the provider says the user did not let the console in), `oauth_code_refused` (the provider did not take the code: used, or not with this sign-in's verifier), `oauth_token_invalid` (Google's ID token: not signed by one of its keys, for another client, from another issuer, expired, or with another nonce), `oauth_provider_unavailable`, `provider_address_unverified` (the provider's address is not verified there: GitHub's primary address, Google's `email_verified`), `provider_link_needed` (a user has the address, and the provider is not where its mail is: control/README.md, 用 GitHub、Google 登录), `provider_linked_elsewhere`, `provider_already_linked`, `invalid_session` (the session that asked for a link has ended), `user_locked`, `rate_limited`. A provider's user that is linked signs in as its user; one that is not is linked by the address only where the provider is the address's mail host (Google for Gmail and its Workspace domain), or makes a user of the address, with no password. The query's parameters are the provider's, and some it adds (Google's `scope`, `authuser`, `prompt`, `hd`): none is refused.",
        "security": [],
        "parameters": [
          {
            "name": "code",
            "in": "query",
            "description": "The provider's code, changed for its word of who the user is with the sign-in's PKCE verifier.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "state",
            "in": "query",
            "description": "The sign-in's state, which finds it.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "error",
            "in": "query",
            "description": "The provider's error, in place of a code (`access_denied`).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "error_description",
            "in": "query",
            "description": "The provider's words of it.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "303": {
            "description": "The console's page: signed in, or told why not.",
            "headers": {
              "Location": {
                "required": true,
                "schema": {
                  "type": "string",
                  "pattern": "^\\S+#/(a/|me|sign-in|$)\\S*$"
                },
                "description": "The console's page (`--console-url`), its route in the fragment."
              },
              "Set-Cookie": {
                "schema": {
                  "type": "string",
                  "pattern": "^((__Host-console|console)=cs_[0-9a-hjkmnp-tv-z]{52}; Path=/; HttpOnly; SameSite=Lax; Max-Age=2592000|(__Host-console-oauth|console-oauth)=; Path=/; HttpOnly; SameSite=Lax; Max-Age=0)(; Secure)?$"
                },
                "description": "The sign-in's cookie, cleared, once its flow was found; and a session's, where it signed in."
              },
              "Referrer-Policy": {
                "required": true,
                "schema": {
                  "type": "string",
                  "const": "no-referrer"
                },
                "description": "The code in the address goes nowhere from here."
              }
            }
          },
          "404": {
            "description": "`no_such_route`: the control plane has no OAuth app of the provider (`--oauth-github-client-id`, `--oauth-google-client-id`), or the path names none such.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "no_such_route"
            ]
          }
        }
      }
    },
    "/v1/auth/connect/login": {
      "get": {
        "operationId": "connectLogin",
        "tags": [
          "Connected apps"
        ],
        "summary": "Hydra asks who signs in",
        "description": "No session needed: the address Hydra sends the browser to for a sign-in (`urls.login`, `<the console's origin>/v1/auth/connect/login`). The browser's session is the user's word: Hydra is told the user (its id is the subject of every token), and the browser goes back to it; nothing is remembered at Hydra, so every connection asks the session. Without a session, the console's sign-in page (`#/sign-in?next=/connect/login?login_challenge=…`), whose sign-in (a password, GitHub or Google) comes back here. A challenge Hydra does not have, or none, is the console's page that says why (`#/connect/error?error=…&message=…`).",
        "security": [],
        "parameters": [
          {
            "name": "login_challenge",
            "in": "query",
            "description": "Hydra's sign-in challenge.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "303": {
            "description": "Where the browser goes next.",
            "headers": {
              "Location": {
                "required": true,
                "schema": {
                  "type": "string",
                  "pattern": "^\\S+$"
                },
                "description": "Where the browser goes next."
              },
              "Referrer-Policy": {
                "schema": {
                  "type": "string",
                  "const": "no-referrer"
                },
                "description": "The challenge in the address goes no further."
              }
            }
          },
          "404": {
            "description": "`no_such_route`: a control plane without an authorization server (`--connect-issuer`) has none of these calls, or no console (`--console false`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "no_such_route"
            ]
          }
        }
      }
    },
    "/v1/auth/connect/consent": {
      "get": {
        "operationId": "connectConsent",
        "tags": [
          "Connected apps"
        ],
        "summary": "Hydra asks for consent",
        "description": "No session needed: the address Hydra sends the browser to for the user's consent (`urls.consent`): the console's page that asks it (`#/connect?consent_challenge=…`, `GET /v1/me/connections/requests/{challenge}`). No challenge: the console's page that says why.",
        "security": [],
        "parameters": [
          {
            "name": "consent_challenge",
            "in": "query",
            "description": "Hydra's consent challenge.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "303": {
            "description": "Where the browser goes next.",
            "headers": {
              "Location": {
                "required": true,
                "schema": {
                  "type": "string",
                  "pattern": "^\\S+#/connect"
                },
                "description": "Where the browser goes next."
              },
              "Referrer-Policy": {
                "schema": {
                  "type": "string",
                  "const": "no-referrer"
                },
                "description": "The challenge in the address goes no further."
              }
            }
          },
          "404": {
            "description": "`no_such_route`: a control plane without an authorization server (`--connect-issuer`) has none of these calls, or no console (`--console false`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "no_such_route"
            ]
          }
        }
      }
    },
    "/v1/auth/connect/error": {
      "get": {
        "operationId": "connectError",
        "tags": [
          "Connected apps"
        ],
        "summary": "What Hydra could not do",
        "description": "No session needed: the address Hydra sends the browser to when it cannot go on (`urls.error`: a client it does not know, a redirect URI the client does not have): the console's page that says it (`#/connect/error?error=…&message=…`). The browser is never sent to a redirect URI that is not the client's.",
        "security": [],
        "parameters": [
          {
            "name": "error",
            "in": "query",
            "description": "Hydra's error.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "error_description",
            "in": "query",
            "description": "Hydra's words of it.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "error_hint",
            "in": "query",
            "description": "Hydra's hint, not shown.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "303": {
            "description": "Where the browser goes next.",
            "headers": {
              "Location": {
                "required": true,
                "schema": {
                  "type": "string",
                  "pattern": "^\\S+#/connect/error"
                },
                "description": "Where the browser goes next."
              },
              "Referrer-Policy": {
                "schema": {
                  "type": "string",
                  "const": "no-referrer"
                },
                "description": "The challenge in the address goes no further."
              }
            }
          },
          "404": {
            "description": "`no_such_route`: a control plane without an authorization server (`--connect-issuer`) has none of these calls, or no console (`--console false`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "no_such_route"
            ]
          }
        }
      }
    },
    "/v1/auth/account-email/verify": {
      "post": {
        "operationId": "verifyAccountEmail",
        "tags": [
          "Console"
        ],
        "summary": "Verify the address of an account's notices, by the link mailed to it",
        "description": "No session (whoever opens the link reads the mailbox, which is what is verified); the console's header and origin. `token`: the token of the link mailed to the address an account set for its notices (`av_...`, `PATCH /v1/account`), which the console's page reads from the fragment of its own address (`#/account-email?token=…`). The address is verified, and the account's notices that waited for it go. A link is taken once; it is revoked when the account's address changes, or is verified another way.",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AccountEmailVerify"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The account and its address, verified.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountEmailVerified"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_json`, `unknown_field`, `unknown_parameter`; `missing_field`: no `token`, an empty one, or one of more than 200 characters; `invalid_field`: a `token` that is not a string.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter",
              "missing_field",
              "invalid_field"
            ]
          },
          "403": {
            "$ref": "#/components/responses/ConsoleCsrf"
          },
          "404": {
            "description": "`token_not_found`: the link is not one this control plane made, has been taken, was revoked (the account's address changed, or was verified another way), or ran out (a week).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "token_not_found"
            ]
          }
        }
      }
    },
    "/v1/auth/email/confirm": {
      "post": {
        "operationId": "confirmEmailChange",
        "tags": [
          "Console"
        ],
        "summary": "A user's new address, by the link mailed to it",
        "description": "No session (the link may be opened in another browser); the console's header and origin. `token`: the token of the link mailed to a new address (`ec_...`, `POST /v1/me/email`), which the console's page reads from the fragment of its own address (`#/email?token=…`). The address is the user's from now, and verified; the old one is mailed that it changed (`email_changed`); the user's sessions go on, and the links mailed to the old address that could still be used (a new password, a verification) are revoked. A link is taken once.",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EmailConfirm"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The user, with its new address.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/User"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_json`, `unknown_field`, `unknown_parameter`; `missing_field`: no `token`, an empty one, or one of more than 200 characters; `invalid_field`: a `token` that is not a string.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter",
              "missing_field",
              "invalid_field"
            ]
          },
          "403": {
            "$ref": "#/components/responses/ConsoleCsrf"
          },
          "404": {
            "description": "`token_not_found`: the link is not one this control plane made, has been taken, was taken over by a newer one, or ran out (24 hours).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "token_not_found"
            ]
          },
          "409": {
            "description": "`email_taken`: another user has the address; the link may be opened again if it is let go.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "email_taken"
            ]
          }
        }
      }
    },
    "/v1/auth/invitation": {
      "post": {
        "operationId": "previewInvitation",
        "tags": [
          "Console"
        ],
        "summary": "What an invitation is, before it is accepted",
        "description": "No session; the console's header and origin. `token`: the token of an invitation's link (`in_...`). Its account, the role, the address it is for and who invited, as its mail says; the invitation is not taken.",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/InvitationAccept"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The invitation.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InvitationPreview"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_json`, `unknown_field`, `unknown_parameter`; `missing_field`: no `token`, an empty one, or one of more than 200 characters; `invalid_field`: a `token` that is not a string.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter",
              "missing_field",
              "invalid_field"
            ]
          },
          "403": {
            "$ref": "#/components/responses/ConsoleCsrf"
          },
          "404": {
            "description": "`invitation_not_found`: no invitation may be taken with the token: none such, taken, revoked, or run out (seven days).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "invitation_not_found"
            ]
          }
        }
      }
    },
    "/v1/auth/sign-in": {
      "post": {
        "operationId": "signIn",
        "tags": [
          "Console"
        ],
        "summary": "Sign in: a session, in a cookie",
        "description": "No session; the console's header and origin. A session for the user of the address, given its password: the answer says it, and its cookie carries it (`Set-Cookie`; `consoleSession`), for thirty days from its last use (`GET /v1/me` gives the cookie again). An address that has no user is answered as a wrong password is (401 `invalid_credentials`), after the same work: a password is checked either way. A user an operator locked is told so (403 `user_locked`) only when its password was right. A user whose address is not verified yet is given a session too, and reads. A password hashed with other parameters than the control plane's is hashed again with them.",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SignIn"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The session, and its cookie.",
            "headers": {
              "Set-Cookie": {
                "required": true,
                "schema": {
                  "type": "string",
                  "pattern": "^(__Host-console|console)=cs_[0-9a-hjkmnp-tv-z]{52}; Path=/; HttpOnly; SameSite=Lax; Max-Age=2592000(; Secure)?$"
                },
                "description": "`__Host-console=<the session>; Path=/; HttpOnly; SameSite=Lax; Max-Age=2592000; Secure`: the session is `cs_` and 52 letters and digits of base32, which no answer has but this header and which the store keeps only the SHA-256 of. On a control plane started with `--console-insecure-cookies true` (a development machine without TLS) the cookie is `console`, without `Secure`."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Session"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_json`, `unknown_field`, `unknown_parameter`; `missing_field`: no `email`, no `password`; `invalid_field`: an `email` that is no address, a `password` of more than 256 characters, a field that is not a string.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter",
              "missing_field",
              "invalid_field"
            ]
          },
          "401": {
            "description": "`invalid_credentials`: the address has no user, or the password is not its user's; which, the answer does not say.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_credentials"
            ]
          },
          "403": {
            "description": "`csrf_header_missing`: no `X-Requested-By: console`; `csrf_origin_mismatch`: an `Origin` that is not the console's (`consoleSession` says which, and why); `user_locked`: an operator locked the user (`POST /internal/users/{id}/lock`), and the password was right.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "csrf_header_missing",
              "csrf_origin_mismatch",
              "user_locked"
            ]
          },
          "429": {
            "description": "`rate_limited` (`retryable`): too many sign-ins of the address for now (10, then one a minute; a check of the current password, `POST /v1/me/password`, counts as one) or from the caller's source (30, then one every 10 seconds), whatever the password, and whether or not the address has a user. The try counts for nothing.",
            "headers": {
              "Retry-After": {
                "required": true,
                "schema": {
                  "type": "string",
                  "pattern": "^[0-9]+$"
                },
                "description": "The seconds until one more try is taken."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "rate_limited"
            ]
          }
        }
      }
    },
    "/v1/auth/reset": {
      "post": {
        "operationId": "requestPasswordReset",
        "tags": [
          "Console"
        ],
        "summary": "A new password by mail: a link that chooses one",
        "description": "No session; the console's header and origin. The user of the address, where there is one that is not locked, is mailed a link that chooses a new password (`reset_password`, an hour: `POST /v1/auth/reset/confirm`). The answer is the same whether or not the address has a user.",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PasswordReset"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Asked for: the same answer whatever the address.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PasswordResetAsked"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_json`, `unknown_field`, `unknown_parameter`; `missing_field`: no `email`; `invalid_field`: an `email` that is no address, or no string.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter",
              "missing_field",
              "invalid_field"
            ]
          },
          "403": {
            "$ref": "#/components/responses/ConsoleCsrf"
          },
          "429": {
            "description": "`rate_limited` (`retryable`): too many resets of the address for now (3, then one every ten minutes) or from the caller's source (10, then one every six minutes), whether or not the address has a user. The try counts for nothing.",
            "headers": {
              "Retry-After": {
                "required": true,
                "schema": {
                  "type": "string",
                  "pattern": "^[0-9]+$"
                },
                "description": "The seconds until one more try is taken."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "rate_limited"
            ]
          }
        }
      }
    },
    "/v1/auth/reset/confirm": {
      "post": {
        "operationId": "confirmPasswordReset",
        "tags": [
          "Console"
        ],
        "summary": "Choose a new password, by the link of a mail",
        "description": "No session; the console's header and origin. `token`: the token of a reset's link (`pr_...`, from the fragment of the page's address it opens); `password`: the new password, 10 to 256 characters, not the address. Every session of the user ends, and its address counts as verified. A password that may not be chosen leaves the link as it was; the link of a user an operator locked since it was mailed is taken, and refused (403 `user_locked`).",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PasswordResetConfirm"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The user, with its new password.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/User"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_json`, `unknown_field`, `unknown_parameter`; `missing_field`: no `token` (or an empty one, or one of more than 200 characters), no `password`; `invalid_field`: a `password` of fewer than 10 or more than 256 characters, or that is the address; a field that is not a string.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter",
              "missing_field",
              "invalid_field"
            ]
          },
          "403": {
            "description": "`csrf_header_missing`: no `X-Requested-By: console`; `csrf_origin_mismatch`: an `Origin` that is not the console's (`consoleSession` says which, and why); `user_locked`: an operator locked the user (the link is taken).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "csrf_header_missing",
              "csrf_origin_mismatch",
              "user_locked"
            ]
          },
          "404": {
            "description": "`token_not_found`: the link is not one this control plane made, has been taken, or ran out (an hour).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "token_not_found"
            ]
          }
        }
      }
    },
    "/v1/me": {
      "get": {
        "operationId": "getMe",
        "tags": [
          "Console"
        ],
        "summary": "The signed-in user, its accounts and its session",
        "description": "The user of the session: its address and whether it is verified, the accounts it is a member of with its role in each (`/v1/accounts/{id}`), and its session, whose cookie the answer gives again (`Set-Cookie`): a session lasts thirty days from its last use, and the browser keeps the cookie thirty days from now.",
        "security": [
          {
            "consoleSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "The user.",
            "headers": {
              "Set-Cookie": {
                "required": true,
                "schema": {
                  "type": "string",
                  "pattern": "^(__Host-console|console)=cs_[0-9a-hjkmnp-tv-z]{52}; Path=/; HttpOnly; SameSite=Lax; Max-Age=2592000(; Secure)?$"
                },
                "description": "The session's cookie again (`POST /v1/auth/sign-in` says what it is), for thirty days from now."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Me"
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`: the call takes none.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter"
            ]
          },
          "401": {
            "$ref": "#/components/responses/NoSession"
          },
          "403": {
            "$ref": "#/components/responses/SessionOnly"
          }
        }
      },
      "delete": {
        "operationId": "deleteMe",
        "tags": [
          "Console"
        ],
        "summary": "The user deletes itself",
        "description": "The signed-in user is deleted, given its password where it has one (as changing the password asks for it): its sessions end (the answer clears the cookie), its memberships and providers go, the links mailed to it that could still be used are revoked, and the user is gone: its address may sign up again, as somebody new. Not while it is the only owner of an account (409 `sole_owner`, which names them: another member made an owner, or the account closed, first). What was recorded of it keeps its id.",
        "security": [
          {
            "consoleSession": []
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UserDelete"
              }
            }
          }
        },
        "responses": {
          "204": {
            "description": "Deleted.",
            "headers": {
              "Set-Cookie": {
                "required": true,
                "schema": {
                  "type": "string",
                  "pattern": "^(__Host-console|console)=; Path=/; HttpOnly; SameSite=Lax; Max-Age=0(; Secure)?$"
                },
                "description": "The session's cookie, cleared."
              }
            }
          },
          "400": {
            "description": "`invalid_json`, `unknown_field`, `unknown_parameter`; `missing_field`: no `password`, of a user that has one; `invalid_field`: a `password` of more than 256 characters, or a field that is not a string.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter",
              "missing_field",
              "invalid_field"
            ]
          },
          "401": {
            "$ref": "#/components/responses/NoSession"
          },
          "403": {
            "description": "`needs_session`: a key; `csrf_header_missing`, `csrf_origin_mismatch` (`consoleSession`); `wrong_password`: `password` is not the user's password.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "needs_session",
              "csrf_header_missing",
              "csrf_origin_mismatch",
              "wrong_password"
            ]
          },
          "409": {
            "description": "`sole_owner`: the user is the only owner of an account; the error's `accounts` are those accounts.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "sole_owner"
            ]
          },
          "429": {
            "description": "`rate_limited` (`retryable`): too many tries of the user's password for now, its sign-ins counted with them (10, then one a minute).",
            "headers": {
              "Retry-After": {
                "required": true,
                "schema": {
                  "type": "string",
                  "pattern": "^[0-9]+$"
                },
                "description": "The seconds until one more try is taken."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "rate_limited"
            ]
          }
        }
      }
    },
    "/v1/me/password": {
      "post": {
        "operationId": "changePassword",
        "tags": [
          "Console"
        ],
        "summary": "A new password, given the current one",
        "description": "The signed-in user chooses a new password (10 to 256 characters, not its address), given its current one. Every other session of the user ends; this one goes on. The check of the current password is a sign-in try of the address, and is limited as one (`POST /v1/auth/sign-in`). A user GitHub or Google made has no password to give: it chooses one by a mail's link (`POST /v1/auth/reset`).",
        "security": [
          {
            "consoleSession": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PasswordChange"
              }
            }
          }
        },
        "responses": {
          "204": {
            "description": "Changed."
          },
          "400": {
            "description": "`invalid_json`, `unknown_field`, `unknown_parameter`; `missing_field`: no `current_password`, no `password`; `invalid_field`: a `current_password` of more than 256 characters, a `password` of fewer than 10 or more than 256 characters or that is the address, a field that is not a string.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter",
              "missing_field",
              "invalid_field"
            ]
          },
          "401": {
            "$ref": "#/components/responses/NoSession"
          },
          "403": {
            "description": "`needs_session`: a key; `csrf_header_missing`: no `X-Requested-By: console`; `csrf_origin_mismatch`: an `Origin` that is not the console's (`consoleSession` says which, and why); `wrong_password`: `current_password` is not the user's password.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "needs_session",
              "csrf_header_missing",
              "csrf_origin_mismatch",
              "wrong_password"
            ]
          },
          "409": {
            "description": "`no_password`: the user has no password (GitHub or Google made it): it chooses one by a mail's link.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "no_password"
            ]
          },
          "429": {
            "description": "`rate_limited` (`retryable`): too many tries of the address for now, its sign-ins counted with them (10, then one a minute).",
            "headers": {
              "Retry-After": {
                "required": true,
                "schema": {
                  "type": "string",
                  "pattern": "^[0-9]+$"
                },
                "description": "The seconds until one more try is taken."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "rate_limited"
            ]
          }
        }
      }
    },
    "/v1/me/verification": {
      "post": {
        "operationId": "resendVerification",
        "tags": [
          "Console"
        ],
        "summary": "Another mail that verifies the address",
        "description": "The signed-in user's address is mailed another link that verifies it (`verify_email`, 24 hours); the links mailed before it work as they did.",
        "security": [
          {
            "consoleSession": []
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "properties": {}
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Mailed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VerificationAsked"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_json`, `unknown_field`, `unknown_parameter`: the call takes no field and no parameter.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter"
            ]
          },
          "401": {
            "$ref": "#/components/responses/NoSession"
          },
          "403": {
            "$ref": "#/components/responses/SessionChange"
          },
          "409": {
            "description": "`already_verified`: the user's address is verified.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "already_verified"
            ]
          },
          "429": {
            "description": "`rate_limited` (`retryable`): three of a user, then one every ten minutes.",
            "headers": {
              "Retry-After": {
                "required": true,
                "schema": {
                  "type": "string",
                  "pattern": "^[0-9]+$"
                },
                "description": "The seconds until one more try is taken."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "rate_limited"
            ]
          }
        }
      }
    },
    "/v1/me/invitations": {
      "post": {
        "operationId": "acceptInvitation",
        "tags": [
          "Console"
        ],
        "summary": "Accept an invitation to an account",
        "description": "The signed-in user accepts an invitation by the token of its link (`in_...`): from now it is a member of the account with the role it was invited as, and its address counts as verified. The invitation has to be for the user's address. An address that has no user signs up through the link instead (`POST /v1/auth/sign-up` with `invitation`).",
        "security": [
          {
            "consoleSession": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/InvitationAccept"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The user, a member of the account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Member"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_json`, `unknown_field`, `unknown_parameter`; `missing_field`: no `token`, an empty one, or one of more than 200 characters; `invalid_field`: a `token` that is not a string.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter",
              "missing_field",
              "invalid_field"
            ]
          },
          "401": {
            "$ref": "#/components/responses/NoSession"
          },
          "403": {
            "description": "`needs_session`: a key; `csrf_header_missing`: no `X-Requested-By: console`; `csrf_origin_mismatch`: an `Origin` that is not the console's (`consoleSession` says which, and why); `invitation_for_another_address`: the invitation is for another address than the user's.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "needs_session",
              "csrf_header_missing",
              "csrf_origin_mismatch",
              "invitation_for_another_address"
            ]
          },
          "404": {
            "description": "`invitation_not_found`: no invitation may be taken with the token: none such, taken, revoked, or run out (seven days).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "invitation_not_found"
            ]
          },
          "409": {
            "description": "`already_member`: the user is a member of the account already.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "already_member"
            ]
          }
        }
      }
    },
    "/v1/me/email": {
      "post": {
        "operationId": "changeEmail",
        "tags": [
          "Console"
        ],
        "summary": "A new address for the user: a link mailed to it",
        "description": "The signed-in user asks for a new address, given its password where it has one. The new address is mailed a link (`ec_...`, 24 hours) that makes it the user's (`POST /v1/auth/email/confirm`); until it is opened nothing changes. Whether another user has the address is not said here: the link says it, to whoever has that mailbox. A new asking takes the place of the one before. A user whose address is not verified may ask too (an address mistyped at sign-up).",
        "security": [
          {
            "consoleSession": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EmailChangeRequest"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Mailed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EmailChange"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_json`, `unknown_field`, `unknown_parameter`; `missing_field`: no `email`; `invalid_field`: an `email` that is no address; `missing_field`: no `password`, of a user that has one; `invalid_field`: a `password` of more than 256 characters, or a field that is not a string.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter",
              "missing_field",
              "invalid_field"
            ]
          },
          "401": {
            "$ref": "#/components/responses/NoSession"
          },
          "403": {
            "description": "`needs_session`: a key; `csrf_header_missing`, `csrf_origin_mismatch` (`consoleSession`); `wrong_password`: `password` is not the user's password.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "needs_session",
              "csrf_header_missing",
              "csrf_origin_mismatch",
              "wrong_password"
            ]
          },
          "409": {
            "description": "`email_unchanged`: it is the user's address already.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "email_unchanged"
            ]
          },
          "429": {
            "description": "`rate_limited` (`retryable`): too many new addresses asked for by the user for now (3, then one every ten minutes), or too many tries of its password (10, then one a minute).",
            "headers": {
              "Retry-After": {
                "required": true,
                "schema": {
                  "type": "string",
                  "pattern": "^[0-9]+$"
                },
                "description": "The seconds until one more try is taken."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "rate_limited"
            ]
          }
        }
      }
    },
    "/v1/me/accounts": {
      "post": {
        "operationId": "createMyAccount",
        "tags": [
          "Console"
        ],
        "summary": "An account of the user's own",
        "description": "A new account, on Free, with the signed-in user its owner, a first project called `default`, and the user's address as where its notices go. One user owns as many accounts on Free as `--free-accounts-per-user` lets it (1 unless the control plane is told otherwise), the one it signed up with among them: an account on a paid plan does not count, nor one it is no owner of.",
        "security": [
          {
            "consoleSession": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AccountRename"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConsoleAccount"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_json`, `unknown_field`, `unknown_parameter`; `invalid_field`: no `name`, or one that is not 1 to 100 characters without control characters.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter",
              "invalid_field"
            ]
          },
          "401": {
            "$ref": "#/components/responses/NoSession"
          },
          "403": {
            "description": "`needs_session`: a key; `csrf_header_missing`, `csrf_origin_mismatch` (`consoleSession`); `email_unverified`: the user's address is not verified yet.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "needs_session",
              "csrf_header_missing",
              "csrf_origin_mismatch",
              "email_unverified"
            ]
          },
          "409": {
            "description": "`too_many_free_accounts`: the user owns as many accounts on Free as one user may; the message names them.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "too_many_free_accounts"
            ]
          }
        }
      }
    },
    "/v1/me/providers": {
      "get": {
        "operationId": "listMyProviders",
        "tags": [
          "Console"
        ],
        "summary": "The providers linked to the user",
        "description": "The providers whose users sign in as the signed-in user, in the order they were linked: one of each provider at the most.",
        "security": [
          {
            "consoleSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "The providers linked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UserProviderList"
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`: the call takes none.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter"
            ]
          },
          "401": {
            "$ref": "#/components/responses/NoSession"
          },
          "403": {
            "$ref": "#/components/responses/SessionOnly"
          }
        }
      }
    },
    "/v1/me/providers/{provider}": {
      "parameters": [
        {
          "name": "provider",
          "in": "path",
          "required": true,
          "description": "`github` or `google`.",
          "schema": {
            "type": "string"
          }
        }
      ],
      "post": {
        "operationId": "linkProvider",
        "tags": [
          "Console"
        ],
        "summary": "Link a provider to the user: the provider's page to go to",
        "description": "As `startOAuth`, for the signed-in user, given its password where it has one: the provider's user that signs in at the provider's page is linked to this user, whatever its address there, unless it is another user's or the user has one of the provider already (the callback says so). The browser that comes back has to be this one, still signed in as the user. The user's address has to be verified. The user is mailed that the provider was linked (`provider_linked`).",
        "security": [
          {
            "consoleSession": []
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ProviderLink"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The provider's page, and the cookie.",
            "headers": {
              "Set-Cookie": {
                "required": true,
                "schema": {
                  "type": "string",
                  "pattern": "^(__Host-console-oauth|console-oauth)=[A-Za-z0-9_-]{43}; Path=/; HttpOnly; SameSite=Lax; Max-Age=600(; Secure)?$"
                },
                "description": "The cookie that binds the sign-in to this browser (`__Host-console-oauth`, ten minutes): the provider's answer is taken only from the browser that has it. `console-oauth`, without `Secure`, on a control plane started with `--console-insecure-cookies true`."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OAuthStarted"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_json`, `unknown_field`, `unknown_parameter`; `missing_field`: no `password`, of a user that has one; `invalid_field`: a `password` of more than 256 characters, or a field that is not a string.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter",
              "missing_field",
              "invalid_field"
            ]
          },
          "401": {
            "$ref": "#/components/responses/NoSession"
          },
          "403": {
            "description": "`needs_session`: a key; `csrf_header_missing`, `csrf_origin_mismatch` (`consoleSession`); `email_unverified`: the user's address is not verified yet; `wrong_password`: `password` is not the user's password.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "needs_session",
              "csrf_header_missing",
              "csrf_origin_mismatch",
              "email_unverified",
              "wrong_password"
            ]
          },
          "404": {
            "description": "`no_such_route`: the control plane has no OAuth app of the provider (`--oauth-github-client-id`, `--oauth-google-client-id`), or the path names none such.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "no_such_route"
            ]
          },
          "409": {
            "description": "`provider_already_linked`: a user of the provider is linked to this user already.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "provider_already_linked"
            ]
          },
          "429": {
            "description": "`rate_limited` (`retryable`): too many tries of the user's password for now, its sign-ins counted with them (10, then one a minute).",
            "headers": {
              "Retry-After": {
                "required": true,
                "schema": {
                  "type": "string",
                  "pattern": "^[0-9]+$"
                },
                "description": "The seconds until one more try is taken."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "rate_limited"
            ]
          },
          "503": {
            "description": "`oauth_provider_unavailable` (`retryable`): Google's discovery document (its endpoints) could not be read, and none was read before.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "oauth_provider_unavailable"
            ]
          }
        }
      },
      "delete": {
        "operationId": "unlinkProvider",
        "tags": [
          "Console"
        ],
        "summary": "Take a provider off the user",
        "description": "The provider's user signs in as the signed-in user no more. Only while another way to sign in is left: a password, or another provider. A provider the control plane no longer has may be taken off too.",
        "security": [
          {
            "consoleSession": []
          }
        ],
        "responses": {
          "204": {
            "description": "Taken off."
          },
          "400": {
            "description": "`unknown_parameter`: the call takes none.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter"
            ]
          },
          "401": {
            "$ref": "#/components/responses/NoSession"
          },
          "403": {
            "$ref": "#/components/responses/SessionChange"
          },
          "404": {
            "description": "`provider_not_linked`: no user of the provider is linked to this user.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "provider_not_linked"
            ]
          },
          "409": {
            "description": "`last_sign_in_method`: it is the only way the user signs in: a password first (`POST /v1/auth/reset` mails a link to choose one), or another provider.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "last_sign_in_method"
            ]
          }
        }
      }
    },
    "/v1/me/connections": {
      "get": {
        "operationId": "listConnections",
        "tags": [
          "Connected apps"
        ],
        "summary": "The user's connected apps",
        "description": "The apps the signed-in user connected with OAuth whose connection is not revoked, the newest first: each with its app (its client at the authorization server, and how it came there: `registered` itself, `document` its metadata document's address, `operator` made by the operator), the account and the projects the grant covers, its scopes, when it was made, last granted (a later consent to the same app grants in its place) and last used.",
        "security": [
          {
            "consoleSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "The connections.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConnectionList"
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`: the call takes none.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter"
            ]
          },
          "401": {
            "$ref": "#/components/responses/NoSession"
          },
          "403": {
            "$ref": "#/components/responses/SessionOnly"
          },
          "404": {
            "description": "`no_such_route`: a control plane without an authorization server (`--connect-issuer`) has none of these calls.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "no_such_route"
            ]
          }
        }
      }
    },
    "/v1/me/connections/{id}": {
      "delete": {
        "operationId": "revokeConnection",
        "tags": [
          "Connected apps"
        ],
        "summary": "Revoke a connected app",
        "description": "Revoked here: every access token of the connection is refused from now (401 `connection_revoked`), whatever it says; the authorization server is told (the user's consent to the client revoked there, with its refresh tokens), and told again every half minute until it has it. The user's record says it (`app_disconnected`).",
        "security": [
          {
            "consoleSession": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The connection's id (`con-…`)."
          }
        ],
        "responses": {
          "204": {
            "description": "Revoked."
          },
          "400": {
            "description": "`unknown_parameter`: the call takes none.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter"
            ]
          },
          "401": {
            "$ref": "#/components/responses/NoSession"
          },
          "403": {
            "$ref": "#/components/responses/SessionChange"
          },
          "404": {
            "description": "`connection_not_found`: the user has no such connection that is not revoked; `no_such_route`: a control plane without an authorization server (`--connect-issuer`) has none of these calls.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "connection_not_found",
              "no_such_route"
            ]
          }
        }
      }
    },
    "/v1/me/connections/requests/{challenge}": {
      "get": {
        "operationId": "getConnectRequest",
        "tags": [
          "Connected apps"
        ],
        "summary": "The consent the authorization server asks for",
        "description": "What the console's page shows of the consent Hydra asks the user for (`GET /v1/auth/connect/consent` sends the browser to the page): the app (its name, how it came to the authorization server, its address), the host it sends the browser back to and whether that is the user's own machine (`loopback`), the scopes it asks for (each with what it opens; one Glac does not grant is `grantable: false`), what its tokens are for (`audience`: the API and the MCP endpoint), and the user it was signed in as.",
        "security": [
          {
            "consoleSession": []
          }
        ],
        "parameters": [
          {
            "name": "challenge",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Hydra's consent challenge."
          }
        ],
        "responses": {
          "200": {
            "description": "The request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConnectRequest"
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`: the call takes none.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter"
            ]
          },
          "401": {
            "$ref": "#/components/responses/NoSession"
          },
          "403": {
            "description": "`needs_session`: a key; `connect_request_of_another_user`: the request was signed in as another user than the session's.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "needs_session",
              "connect_request_of_another_user"
            ]
          },
          "404": {
            "description": "`connect_request_not_found`: Hydra has no such request (half an hour old, or answered); `no_such_route`: a control plane without an authorization server (`--connect-issuer`) has none of these calls.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "connect_request_not_found",
              "no_such_route"
            ]
          },
          "503": {
            "description": "`authorization_server_unavailable`: Hydra could not be asked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "authorization_server_unavailable"
            ]
          }
        }
      }
    },
    "/v1/me/connections/requests/{challenge}/accept": {
      "post": {
        "operationId": "acceptConnectRequest",
        "tags": [
          "Connected apps"
        ],
        "summary": "Consent: connect the app",
        "description": "The user consents: the app is connected to the account and the projects named, with the scopes named (of those it asked for: Glac's five, and Hydra's `offline_access` and `openid`). The connection of the user to the app is made, or, where there is one, granted these in place of what it had; Hydra is told to issue the app its code, every token of it for the API and the MCP endpoint and carrying the connection's id. A user whose address is not verified consents to nothing; `billing:read` is granted by an owner alone. The user's record says it (`app_connected`). The console sends the browser to `redirect_to`.",
        "security": [
          {
            "consoleSession": []
          }
        ],
        "parameters": [
          {
            "name": "challenge",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Hydra's consent challenge."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ConnectAccept"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Connected: where the browser goes, and the connection.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConnectAnswer"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_json`, `unknown_field`, `unknown_parameter`, `missing_field` (`account`), `invalid_field` (`projects`, `scopes`: lists of one to a hundred strings); `scope_not_asked`: a scope the app did not ask for, one Glac does not grant, or none of the API's.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter",
              "missing_field",
              "invalid_field",
              "scope_not_asked"
            ]
          },
          "401": {
            "$ref": "#/components/responses/NoSession"
          },
          "403": {
            "description": "`needs_session`, `csrf_header_missing`, `csrf_origin_mismatch` (`consoleSession`); `email_unverified`; `role_insufficient`: `billing:read` by a user who is no owner of the account; `connect_request_of_another_user`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "needs_session",
              "csrf_header_missing",
              "csrf_origin_mismatch",
              "email_unverified",
              "role_insufficient",
              "connect_request_of_another_user"
            ]
          },
          "404": {
            "description": "`account_not_found`: the user is no member of the account; `project_not_found`: the account has no such project, or it is named twice; `connect_request_not_found`; `no_such_route`: a control plane without an authorization server (`--connect-issuer`) has none of these calls.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "account_not_found",
              "project_not_found",
              "connect_request_not_found",
              "no_such_route"
            ]
          },
          "503": {
            "description": "`authorization_server_unavailable`: Hydra could not be asked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "authorization_server_unavailable"
            ]
          }
        }
      }
    },
    "/v1/me/connections/requests/{challenge}/refuse": {
      "post": {
        "operationId": "refuseConnectRequest",
        "tags": [
          "Connected apps"
        ],
        "summary": "Refuse the app",
        "description": "The user refuses: Hydra sends the app `access_denied`, and nothing is connected. The user's record says it (`app_refused`). The console sends the browser to `redirect_to`.",
        "security": [
          {
            "consoleSession": []
          }
        ],
        "parameters": [
          {
            "name": "challenge",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Hydra's consent challenge."
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "properties": {}
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Refused: where the browser goes.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConnectAnswer"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_json`, `unknown_field`, `unknown_parameter`: the call takes nothing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter"
            ]
          },
          "401": {
            "$ref": "#/components/responses/NoSession"
          },
          "403": {
            "description": "`needs_session`, `csrf_header_missing`, `csrf_origin_mismatch` (`consoleSession`); `connect_request_of_another_user`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "needs_session",
              "csrf_header_missing",
              "csrf_origin_mismatch",
              "connect_request_of_another_user"
            ]
          },
          "404": {
            "description": "`connect_request_not_found`; `no_such_route`: a control plane without an authorization server (`--connect-issuer`) has none of these calls.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "connect_request_not_found",
              "no_such_route"
            ]
          },
          "503": {
            "description": "`authorization_server_unavailable`: Hydra could not be asked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "authorization_server_unavailable"
            ]
          }
        }
      }
    },
    "/v1/me/audit": {
      "get": {
        "operationId": "listMyAudit",
        "tags": [
          "Console"
        ],
        "summary": "The signed-in user's record",
        "description": "What happened to the user, the newest first (control/README.md, 记下来的): its sign-up and the sign-ups again with its address, its sign-ins and the failed ones (`data.reason`: `wrong_password`, `locked`), the mails it asked for, its address verified, its password reset and changed and the tries that failed, its sessions ended and its signing out, an operator's locks, and what came of it as a member of accounts (joined, its role changed, removed, left, made an owner by the operator). Each with where its call came from and the call's id, kept as long as the audit log keeps an entry (`--audit-keep-days`).",
        "security": [
          {
            "consoleSession": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "The entries, the newest first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UserAuditList"
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`, `invalid_field` (`limit`), `invalid_cursor`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter",
              "invalid_field",
              "invalid_cursor"
            ]
          },
          "401": {
            "$ref": "#/components/responses/NoSession"
          },
          "403": {
            "$ref": "#/components/responses/SessionOnly"
          }
        }
      }
    },
    "/v1/me/sessions": {
      "get": {
        "operationId": "listSessions",
        "tags": [
          "Console"
        ],
        "summary": "The signed-in user's sessions",
        "description": "Its sessions that have not ended or run out, the newest first: when each was made and last used, until when it lasts if it is not used again, the address and the browser it was made from, and which is this one (`current`).",
        "security": [
          {
            "consoleSession": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "The sessions, the newest first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SessionList"
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`, `invalid_field` (`limit`), `invalid_cursor`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter",
              "invalid_field",
              "invalid_cursor"
            ]
          },
          "401": {
            "$ref": "#/components/responses/NoSession"
          },
          "403": {
            "$ref": "#/components/responses/SessionOnly"
          }
        }
      },
      "delete": {
        "operationId": "endOtherSessions",
        "tags": [
          "Console"
        ],
        "summary": "End every session but this one",
        "description": "Every other session of the signed-in user ends: its cookie opens nothing from now (401 `invalid_session`).",
        "security": [
          {
            "consoleSession": []
          }
        ],
        "responses": {
          "204": {
            "description": "Ended."
          },
          "400": {
            "description": "`unknown_parameter`: the call takes none.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter"
            ]
          },
          "401": {
            "$ref": "#/components/responses/NoSession"
          },
          "403": {
            "$ref": "#/components/responses/SessionChange"
          }
        }
      }
    },
    "/v1/me/sessions/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "The session's id (`ses-...`).",
          "schema": {
            "type": "string"
          }
        }
      ],
      "delete": {
        "operationId": "endSession",
        "tags": [
          "Console"
        ],
        "summary": "End a session, or sign out",
        "description": "One of the signed-in user's sessions ends: its cookie opens nothing from now (401 `invalid_session`). Of this one it is signing out, and the answer clears the cookie (`Set-Cookie` with `Max-Age=0`).",
        "security": [
          {
            "consoleSession": []
          }
        ],
        "responses": {
          "204": {
            "description": "Ended.",
            "headers": {
              "Set-Cookie": {
                "schema": {
                  "type": "string",
                  "pattern": "^(__Host-console|console)=; Path=/; HttpOnly; SameSite=Lax; Max-Age=0(; Secure)?$"
                },
                "description": "Of this session: its cookie, cleared."
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`: the call takes none.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter"
            ]
          },
          "401": {
            "$ref": "#/components/responses/NoSession"
          },
          "403": {
            "$ref": "#/components/responses/SessionChange"
          },
          "404": {
            "description": "`session_not_found`: the user has no session of that id that has not ended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "session_not_found"
            ]
          }
        }
      }
    },
    "/v1/accounts/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "The account's id (`acct-...`): one of `accounts` of `GET /v1/me`.",
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "operationId": "getConsoleAccount",
        "tags": [
          "Console"
        ],
        "summary": "An account the signed-in user is a member of",
        "description": "The account, with the signed-in user's role in it. An account it is no member of is not there for it (404 `account_not_found`), and neither is a closed one, which has no members. Every role.",
        "security": [
          {
            "consoleSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "The account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConsoleAccount"
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`: the call takes none.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter"
            ]
          },
          "401": {
            "$ref": "#/components/responses/NoSession"
          },
          "403": {
            "$ref": "#/components/responses/SessionOnly"
          },
          "404": {
            "description": "`account_not_found`: the user is no member of such an account (a closed account has no members).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "account_not_found"
            ]
          }
        }
      },
      "patch": {
        "operationId": "renameAccount",
        "tags": [
          "Console"
        ],
        "summary": "Rename the account",
        "description": "What the account is called: 1 to 100 characters. Its owners and admins.",
        "security": [
          {
            "consoleSession": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AccountRename"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The account, renamed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConsoleAccount"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_json`, `unknown_field`, `unknown_parameter`; `invalid_field`: no `name` of 1 to 100 characters with no control character among them.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter",
              "invalid_field"
            ]
          },
          "401": {
            "$ref": "#/components/responses/NoSession"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt",
            "description": "`account_in_debt`: the account owes: every call of it that is not a `GET` is refused so, leaving it but, with the words its keys are told (how much, since when, how to pay)."
          },
          "403": {
            "description": "`needs_session`: a key; `csrf_header_missing`, `csrf_origin_mismatch`: no `X-Requested-By: console`, or an `Origin` that is not the console's; `email_unverified`: the user's address is not verified yet; `account_held`: the account is held for its payments (every call of it that is not a `GET` is refused so, leaving it but); `role_insufficient`: a member.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "needs_session",
              "csrf_header_missing",
              "csrf_origin_mismatch",
              "email_unverified",
              "role_insufficient",
              "account_held"
            ]
          },
          "404": {
            "description": "`account_not_found`: the user is no member of such an account (a closed account has no members).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "account_not_found"
            ]
          }
        }
      },
      "delete": {
        "operationId": "closeAccount",
        "tags": [
          "Console"
        ],
        "summary": "Close the account",
        "description": "The account is closed (control/README.md, 关掉账户): its owners alone. At once no member is left in it, its invitations and every key of its projects are revoked, and its domains are let go (another account may register the same names). Then what it stores is deleted as for a debt (control/README.md, 欠费的账户): its apps with their versions and instances, its sandboxes, snapshots and volumes, the templates of its own on the nodes, each on its node, by its id, in its project's name, a pass every five seconds until nothing is left (a node that is away keeps its part until it is back); the project's audit log says each was deleted by the platform (`actor`: `{type: \"platform\", id: <the account's id>}`). What the books need stays: the account and its projects, closed, its usage, statements, ledger and charges, its audit logs and notices; and its secrets, SSH keys, webhooks and variables, which nothing is left to use. Its Credits lapse on their own dates. A closed account is not opened again; its users stay, and may be members of other accounts. Not while it owes (402 `account_in_debt`), while it is held for its payments (403 `account_held`), or while it is on a paid plan (409 `account_on_paid_plan`).",
        "security": [
          {
            "consoleSession": []
          }
        ],
        "responses": {
          "202": {
            "description": "Closed: what it stores is being deleted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountClosure"
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`; `invalid_json`, `unknown_field`: a body, which the call takes none of.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter",
              "invalid_json",
              "unknown_field"
            ]
          },
          "401": {
            "$ref": "#/components/responses/NoSession"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt",
            "description": "`account_in_debt`: the account owes: every call of it that is not a `GET` is refused so, leaving it but, with the words its keys are told (how much, since when, how to pay)."
          },
          "403": {
            "description": "`needs_session`: a key; `csrf_header_missing`, `csrf_origin_mismatch`: no `X-Requested-By: console`, or an `Origin` that is not the console's; `email_unverified`: the user's address is not verified yet; `account_held`: the account is held for its payments (every call of it that is not a `GET` is refused so, leaving it but); `role_insufficient`: an admin or a member.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "needs_session",
              "csrf_header_missing",
              "csrf_origin_mismatch",
              "email_unverified",
              "role_insufficient",
              "account_held"
            ]
          },
          "404": {
            "description": "`account_not_found`: the user is no member of such an account (a closed account has no members).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "account_not_found"
            ]
          },
          "409": {
            "description": "`account_on_paid_plan`: the account is on a paid plan, whose next period would be charged: moved to Free first (`PATCH /v1/billing` with `plan`), it is closed once the period it paid for is over.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "account_on_paid_plan"
            ]
          }
        }
      }
    },
    "/v1/accounts/{id}/projects": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "The account's id (`acct-...`): one of `accounts` of `GET /v1/me`.",
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "operationId": "listAccountProjects",
        "tags": [
          "Console"
        ],
        "summary": "The account's projects",
        "description": "The account's projects, the newest first. A project's spend limit is `/v1/limits`, called with the session and the project named (`X-Account`, `X-Project`), as everything else of a project is. Every role.",
        "security": [
          {
            "consoleSession": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "The projects, the newest first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountProjectList"
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`, `invalid_field` (`limit`), `invalid_cursor`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter",
              "invalid_field",
              "invalid_cursor"
            ]
          },
          "401": {
            "$ref": "#/components/responses/NoSession"
          },
          "403": {
            "$ref": "#/components/responses/SessionOnly"
          },
          "404": {
            "description": "`account_not_found`: the user is no member of such an account (a closed account has no members).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "account_not_found"
            ]
          }
        }
      },
      "post": {
        "operationId": "createAccountProject",
        "tags": [
          "Console"
        ],
        "summary": "Make a project of the account",
        "description": "Another project of the account. Every role. An account has 100 projects at the most; a project is not deleted, and is closed with its account.",
        "security": [
          {
            "consoleSession": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AccountProjectCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The project.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountProject"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_json`, `unknown_field`, `unknown_parameter`; `invalid_field`: no `name` of 1 to 100 characters with no control character among them.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter",
              "invalid_field"
            ]
          },
          "401": {
            "$ref": "#/components/responses/NoSession"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt",
            "description": "`account_in_debt`: the account owes: every call of it that is not a `GET` is refused so, leaving it but, with the words its keys are told (how much, since when, how to pay)."
          },
          "403": {
            "description": "`needs_session`: a key; `csrf_header_missing`, `csrf_origin_mismatch`: no `X-Requested-By: console`, or an `Origin` that is not the console's; `email_unverified`: the user's address is not verified yet; `account_held`: the account is held for its payments (every call of it that is not a `GET` is refused so, leaving it but).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "needs_session",
              "csrf_header_missing",
              "csrf_origin_mismatch",
              "email_unverified",
              "account_held"
            ]
          },
          "404": {
            "description": "`account_not_found`: the user is no member of such an account (a closed account has no members).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "account_not_found"
            ]
          },
          "409": {
            "description": "`too_many_projects`: the account has 100 projects.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "too_many_projects"
            ]
          }
        }
      }
    },
    "/v1/accounts/{id}/members": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "The account's id (`acct-...`): one of `accounts` of `GET /v1/me`.",
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "operationId": "listMembers",
        "tags": [
          "Console"
        ],
        "summary": "The account's members",
        "description": "The account's members, the newest first, each with its role and whether its address is verified. Every role.",
        "security": [
          {
            "consoleSession": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "The members, the newest first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MemberList"
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`, `invalid_field` (`limit`), `invalid_cursor`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter",
              "invalid_field",
              "invalid_cursor"
            ]
          },
          "401": {
            "$ref": "#/components/responses/NoSession"
          },
          "403": {
            "$ref": "#/components/responses/SessionOnly"
          },
          "404": {
            "description": "`account_not_found`: the user is no member of such an account (a closed account has no members).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "account_not_found"
            ]
          }
        }
      },
      "post": {
        "operationId": "inviteMember",
        "tags": [
          "Console"
        ],
        "summary": "Invite an address into the account",
        "description": "An invitation of an address into the account with a role: the address is mailed a link (`invitation`, seven days), which says who invites it. An address that has a user signs in and accepts it (`POST /v1/me/invitations`); one that has none signs up through it (`POST /v1/auth/sign-up` with `invitation`). An invitation of an address takes the place of the one it had of the account. Owners invite any role, admins `admin` and `member`, members nobody.",
        "security": [
          {
            "consoleSession": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MemberInvite"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The invitation, mailed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Invitation"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_json`, `unknown_field`, `unknown_parameter`; `missing_field`: no `email`; `invalid_field`: an `email` that is no address, a `role` that is none of `owner`, `admin` and `member`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter",
              "missing_field",
              "invalid_field"
            ]
          },
          "401": {
            "$ref": "#/components/responses/NoSession"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt",
            "description": "`account_in_debt`: the account owes: every call of it that is not a `GET` is refused so, leaving it but, with the words its keys are told (how much, since when, how to pay)."
          },
          "403": {
            "description": "`needs_session`: a key; `csrf_header_missing`, `csrf_origin_mismatch`: no `X-Requested-By: console`, or an `Origin` that is not the console's; `email_unverified`: the user's address is not verified yet; `account_held`: the account is held for its payments (every call of it that is not a `GET` is refused so, leaving it but); `role_insufficient`: a member; an admin that invites an owner.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "needs_session",
              "csrf_header_missing",
              "csrf_origin_mismatch",
              "email_unverified",
              "role_insufficient",
              "account_held"
            ]
          },
          "404": {
            "description": "`account_not_found`: the user is no member of such an account (a closed account has no members).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "account_not_found"
            ]
          },
          "409": {
            "description": "`already_member`: the address is a member's of the account already.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "already_member"
            ]
          },
          "429": {
            "description": "`rate_limited` (`retryable`): 20 invitations of an account, then one every three minutes.",
            "headers": {
              "Retry-After": {
                "required": true,
                "schema": {
                  "type": "string",
                  "pattern": "^[0-9]+$"
                },
                "description": "The seconds until one more try is taken."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "rate_limited"
            ]
          }
        }
      }
    },
    "/v1/accounts/{id}/members/{user}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "The account's id (`acct-...`): one of `accounts` of `GET /v1/me`.",
          "schema": {
            "type": "string"
          }
        },
        {
          "name": "user",
          "in": "path",
          "required": true,
          "description": "The member's user id (`usr-...`).",
          "schema": {
            "type": "string"
          }
        }
      ],
      "patch": {
        "operationId": "updateMember",
        "tags": [
          "Console"
        ],
        "summary": "Change a member's role",
        "description": "A member's role from now. Owners change any; admins those of admins and members, and make nobody an owner. The last owner is not made anything else (409 `last_owner`).",
        "security": [
          {
            "consoleSession": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MemberUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The member, with its role.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Member"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_json`, `unknown_field`, `unknown_parameter`; `invalid_field`: no `role` that is one of `owner`, `admin` and `member`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter",
              "invalid_field"
            ]
          },
          "401": {
            "$ref": "#/components/responses/NoSession"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt",
            "description": "`account_in_debt`: the account owes: every call of it that is not a `GET` is refused so, leaving it but, with the words its keys are told (how much, since when, how to pay)."
          },
          "403": {
            "description": "`needs_session`: a key; `csrf_header_missing`, `csrf_origin_mismatch`: no `X-Requested-By: console`, or an `Origin` that is not the console's; `email_unverified`: the user's address is not verified yet; `account_held`: the account is held for its payments (every call of it that is not a `GET` is refused so, leaving it but); `role_insufficient`: a member; an admin that changes an owner's role, or makes one.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "needs_session",
              "csrf_header_missing",
              "csrf_origin_mismatch",
              "email_unverified",
              "role_insufficient",
              "account_held"
            ]
          },
          "404": {
            "description": "`account_not_found`: the user is no member of such an account; `member_not_found`: the account has no such member.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "account_not_found",
              "member_not_found"
            ]
          },
          "409": {
            "description": "`last_owner`: the account would be left without an owner.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "last_owner"
            ]
          }
        }
      },
      "delete": {
        "operationId": "removeMember",
        "tags": [
          "Console"
        ],
        "summary": "Remove a member, or leave the account",
        "description": "A member out of the account. Of the signed-in user itself it is leaving the account, which every role may do but the last owner (409 `last_owner`), and which an account that owes or is held lets it do. Owners remove anybody, admins admins and members.",
        "security": [
          {
            "consoleSession": []
          }
        ],
        "responses": {
          "204": {
            "description": "Out of the account."
          },
          "400": {
            "description": "`unknown_parameter`: the call takes none.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter"
            ]
          },
          "401": {
            "$ref": "#/components/responses/NoSession"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt",
            "description": "`account_in_debt`: the account owes: every call of it that is not a `GET` is refused so, leaving it but, with the words its keys are told (how much, since when, how to pay)."
          },
          "403": {
            "description": "`needs_session`: a key; `csrf_header_missing`, `csrf_origin_mismatch`: no `X-Requested-By: console`, or an `Origin` that is not the console's; `email_unverified`: the user's address is not verified yet; `account_held`: the account is held for its payments (every call of it that is not a `GET` is refused so, leaving it but); `role_insufficient`: a member that removes another; an admin that removes an owner.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "needs_session",
              "csrf_header_missing",
              "csrf_origin_mismatch",
              "email_unverified",
              "role_insufficient",
              "account_held"
            ]
          },
          "404": {
            "description": "`account_not_found`: the user is no member of such an account; `member_not_found`: the account has no such member.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "account_not_found",
              "member_not_found"
            ]
          },
          "409": {
            "description": "`last_owner`: the account would be left without an owner.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "last_owner"
            ]
          }
        }
      }
    },
    "/v1/accounts/{id}/invitations": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "The account's id (`acct-...`): one of `accounts` of `GET /v1/me`.",
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "operationId": "listInvitations",
        "tags": [
          "Console"
        ],
        "summary": "The account's invitations that may still be taken",
        "description": "The invitations of the account that have not been taken, revoked or run out, the newest first. Its owners and admins.",
        "security": [
          {
            "consoleSession": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "The invitations, the newest first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InvitationList"
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`, `invalid_field` (`limit`), `invalid_cursor`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter",
              "invalid_field",
              "invalid_cursor"
            ]
          },
          "401": {
            "$ref": "#/components/responses/NoSession"
          },
          "403": {
            "description": "`needs_session`: a key; `role_insufficient`: a member.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "needs_session",
              "role_insufficient"
            ]
          },
          "404": {
            "description": "`account_not_found`: the user is no member of such an account (a closed account has no members).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "account_not_found"
            ]
          }
        }
      }
    },
    "/v1/accounts/{id}/invitations/{invitation}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "The account's id (`acct-...`): one of `accounts` of `GET /v1/me`.",
          "schema": {
            "type": "string"
          }
        },
        {
          "name": "invitation",
          "in": "path",
          "required": true,
          "description": "The invitation's id (`inv-...`).",
          "schema": {
            "type": "string"
          }
        }
      ],
      "delete": {
        "operationId": "revokeInvitation",
        "tags": [
          "Console"
        ],
        "summary": "Revoke an invitation",
        "description": "The invitation's link opens nothing from now. Its owners and admins.",
        "security": [
          {
            "consoleSession": []
          }
        ],
        "responses": {
          "204": {
            "description": "Revoked."
          },
          "400": {
            "description": "`unknown_parameter`: the call takes none.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter"
            ]
          },
          "401": {
            "$ref": "#/components/responses/NoSession"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt",
            "description": "`account_in_debt`: the account owes: every call of it that is not a `GET` is refused so, leaving it but, with the words its keys are told (how much, since when, how to pay)."
          },
          "403": {
            "description": "`needs_session`: a key; `csrf_header_missing`, `csrf_origin_mismatch`: no `X-Requested-By: console`, or an `Origin` that is not the console's; `email_unverified`: the user's address is not verified yet; `account_held`: the account is held for its payments (every call of it that is not a `GET` is refused so, leaving it but); `role_insufficient`: a member.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "needs_session",
              "csrf_header_missing",
              "csrf_origin_mismatch",
              "email_unverified",
              "role_insufficient",
              "account_held"
            ]
          },
          "404": {
            "description": "`account_not_found`: the user is no member of such an account; `invitation_not_found`: the account has no such invitation that may still be taken: none, taken, revoked, or run out.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "account_not_found",
              "invitation_not_found"
            ]
          }
        }
      }
    },
    "/v1/accounts/{id}/audit": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "The account's id (`acct-...`): one of `accounts` of `GET /v1/me`.",
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "operationId": "listAccountAudit",
        "tags": [
          "Console"
        ],
        "summary": "The account's record",
        "description": "What happened to the account's members, the newest first (control/README.md, 记下来的): who invited whom, who joined, whose role changed, who was removed and who left, the invitations revoked, the owners the operator attached, the account renamed, its projects made, and its closing; who did it, from where, under which call. Its owners and admins.",
        "security": [
          {
            "consoleSession": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "The entries, the newest first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UserAuditList"
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`, `invalid_field` (`limit`), `invalid_cursor`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter",
              "invalid_field",
              "invalid_cursor"
            ]
          },
          "401": {
            "$ref": "#/components/responses/NoSession"
          },
          "403": {
            "description": "`needs_session`: a key; `role_insufficient`: a member.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "needs_session",
              "role_insufficient"
            ]
          },
          "404": {
            "description": "`account_not_found`: the user is no member of such an account (a closed account has no members).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "account_not_found"
            ]
          }
        }
      }
    },
    "/v1/domains": {
      "post": {
        "operationId": "registerDomain",
        "tags": [
          "Domains"
        ],
        "summary": "Register a domain for a port of a sandbox, or for an app",
        "description": "The name is kept in lower case and without the dot a fully qualified name may end with: `App.Example.com.` and `app.example.com` are one name. An international name is given in its ASCII form (`xn--...`). The body names what the domain leads to: `sandbox` and `port`, or `app`, and not both. The sandbox or the app is named by id or by name, and kept by id. The answer says which DNS records prove the name; the proof is looked for at once, then at growing intervals (5 seconds, doubling, an hour at the most) for seven days.",
        "security": [
          {
            "apiKey": [
              "admin"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DomainCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The registration, `pending_verification`.",
            "headers": {
              "Location": {
                "schema": {
                  "type": "string"
                },
                "description": "`/v1/domains/{id}`."
              },
              "Idempotent-Replayed": {
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                },
                "description": "Present when the answer is the one of an earlier request with the same `Idempotency-Key`."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Domain"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_json`, `unknown_field`, `unknown_parameter`; `missing_field`: no `domain`, or neither `app` nor `sandbox` and `port`; `invalid_field`: a field of the wrong type, a port out of range, `app` together with `sandbox` or `port` (`param` names the one that came besides); `invalid_domain`: not a domain name (labels of letters, digits and hyphens, two or more, 253 characters at the most, not an address, not of the form of a preview URL's first label); `wildcard_not_supported`: `*.example.com`; `domain_reserved`: a name of the platform's own, or under one.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter",
              "missing_field",
              "invalid_field",
              "invalid_domain",
              "wildcard_not_supported",
              "domain_reserved",
              "invalid_idempotency_key"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "description": "`key_scope`: the key does not allow `admin`; `needs_api_key`: a sandbox token or the operator's key; `domain_limit_reached`: the project has as many domains as a project may have (100 unless the control plane is told otherwise); `account_held`: the account is held for its payments, and every call of its keys but `GET /v1/account` is refused so, before the scopes are looked at (`x-error-codes`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "key_scope",
              "needs_api_key",
              "domain_limit_reached",
              "account_held"
            ]
          },
          "404": {
            "description": "`sandbox_not_found`: the project has no such sandbox. `app_not_found`: the project has no such app.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "sandbox_not_found",
              "app_not_found"
            ]
          },
          "409": {
            "description": "`domain_exists`: this project has registered the name already (another project's registration of it is no obstacle); `idempotency_key_reused`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "domain_exists",
              "idempotency_key_reused"
            ]
          }
        }
      },
      "get": {
        "operationId": "listDomains",
        "tags": [
          "Domains"
        ],
        "summary": "List the project's domains",
        "security": [
          {
            "apiKey": [
              "read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "name": "sandbox_id",
            "in": "query",
            "description": "Only the domains that lead to this sandbox, named by id or by name: which of its ports are open to everybody under a name.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "app_id",
            "in": "query",
            "description": "Only the domains that lead to this app, named by id or by name.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The domains, newest first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DomainList"
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`, `invalid_field` (`limit`), `invalid_cursor`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter",
              "invalid_field",
              "invalid_cursor"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          }
        }
      }
    },
    "/v1/domains/{domain}": {
      "parameters": [
        {
          "name": "domain",
          "in": "path",
          "required": true,
          "description": "The registration's id (`dom-...`), or its name in any spelling.",
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "operationId": "getDomain",
        "tags": [
          "Domains"
        ],
        "summary": "One domain",
        "security": [
          {
            "apiKey": [
              "read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "The registration as it is now.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Domain"
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`: the call takes none.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          },
          "404": {
            "$ref": "#/components/responses/NoSuchDomain"
          }
        }
      },
      "patch": {
        "operationId": "pointDomain",
        "tags": [
          "Domains"
        ],
        "summary": "Point a domain at another port, another sandbox or an app",
        "description": "The name, its proof and its certificate stay as they are; requests go to the new port, or to the app, from the moment the edge has the change. `port` alone is another port of the same sandbox; `app` points a sandbox's domain at an app, and `sandbox` with `port` an app's domain at a sandbox. A detached registration is attached again.",
        "security": [
          {
            "apiKey": [
              "admin"
            ]
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DomainUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The registration after the change.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Domain"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_json`, `unknown_field`, `unknown_parameter`; `invalid_field`: a field of the wrong type, a port out of range, `app` together with `sandbox` or `port`; `missing_field`: for a domain that leads to an app, `port` without `sandbox` or `sandbox` without `port` (nothing of an app's domain stays when it is pointed at a sandbox).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter",
              "invalid_field",
              "missing_field"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          },
          "404": {
            "description": "`domain_not_found`; `sandbox_not_found`: the project has no such sandbox; `app_not_found`: the project has no such app.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "domain_not_found",
              "sandbox_not_found",
              "app_not_found"
            ]
          }
        }
      },
      "delete": {
        "operationId": "deleteDomain",
        "tags": [
          "Domains"
        ],
        "summary": "Delete a domain",
        "description": "Its requests are refused from the moment the edge has the change (404 `no_such_domain`), and no certificate is obtained or renewed for the name any more. The certificate it had is not revoked: it is valid until it runs out, and the edge may go on showing it until it forgets it (at the latest when it would have renewed it, or when it restarts). The same project registering the name again is proved by the same record.",
        "security": [
          {
            "apiKey": [
              "admin"
            ]
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted."
          },
          "400": {
            "description": "`unknown_parameter`: the call takes none.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          },
          "404": {
            "$ref": "#/components/responses/NoSuchDomain"
          }
        }
      }
    },
    "/v1/account": {
      "get": {
        "operationId": "getAccount",
        "tags": [
          "Account"
        ],
        "summary": "The account of the key's project, and its traffic to the internet this month",
        "description": "The node's `GET /v1/account` (`sandboxd/openapi/openapi.json`) as the control plane answers it: which account and project the key is of, where the account's notices go and in which language, what it owes, whether it is held for its payments (`payment_hold`: the one call such an account is answered), where it pays from (`billing_address`, `tax_id`) and how what it buys is sold there (`tax`: control/README.md, 税), and its traffic to the internet added up over all its projects and all the nodes, against the allowance of the stretch it is in. An account that owes may read it. Takes `read`.",
        "security": [
          {
            "apiKey": [
              "read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "The account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Account"
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "description": "`key_scope`, `needs_api_key`, as everywhere. (An account held for its payments is answered this call, which says so: `payment_hold`.)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "key_scope",
              "needs_api_key"
            ]
          }
        }
      },
      "patch": {
        "operationId": "updateAccount",
        "tags": [
          "Account"
        ],
        "summary": "Set where the account's notices go and their language, its billing address and tax id, and its cap on traffic to the internet",
        "description": "What is given changes, and nothing else. `email`: where the account's notices are sent; `null` takes the address away. It is verified at once where it is the verified one the account has, or the verified address of a member of the account; otherwise a link that verifies it is mailed to it, and the account's notices wait until it is opened (`email_verified`, `POST /v1/auth/account-email/verify`); setting it again mails another. Those links are three of an account, then one every ten minutes (429 `rate_limited`, before anything of the call is done). `language`: the language the notices are written in, one of theirs (`en`, `de`, `fr`; 400 `invalid_field` otherwise), or `null` for the one most people of its billing address's country read first (`language` of `GET /v1/account` says which, and why). An account that owes may change these two, and its billing address and tax id, when they are all the body holds (402 `account_in_debt` otherwise): paying needs them where only businesses are sold to. `egress_overage_cap_bytes`: the most bytes in a calendar month the account sends, counted from the month's first byte on any plan and whatever it holds; `null` takes the cap away. `egress_overage` says how things stand and is not the account's to choose: what it is now changes nothing; `pay` from an account that is not on Paid or holds no balance is 403 `needs_balance`, `stop` from one on Paid that holds one is 400 `invalid_field` (a cap is what stops traffic at a number of bytes). `billing_address`: where the account pays from, which its payments are taxed by (control/README.md, 税): `country` (two letters, ISO 3166-1) and as many of `line1`, `line2`, `city`, `state` and `postal_code` as there are (of the US the state and the ZIP code, of Canada the province or the postal code, which the tax there goes by); `null` takes it away. `tax_id`: a business's tax id as Stripe names it, `type` (`eu_vat`, `gb_vat`, `au_abn`, ...) and `value` (an EU VAT number with its country's two letters before it: `DE123456789`); `null` takes it away. Where Stripe collects, both are set on the account's customer at Stripe first, and what Stripe refuses of them is refused (400 `invalid_field`) and not set; Stripe verifies the tax id with its authority (VIES, HMRC, the ABR): it is `pending` until Stripe says what it came to (`tax_id.updated`), and a tax id that is not verified is taken off the customer again. Where what stands in for a collector collects, it verifies the tax id at once, as it is told to (`PATCH /internal/charges/collector`). An account whose tax id is verified, or still being verified, is a business: it can pay where only businesses are sold to (`tax.can_pay`), and where the platform is registered what it pays is reverse charged (no tax is added). The account's own word for its address confirms it for the payments it was asked to confirm it for (the notice `tax_address_confirm`). Takes `admin`.",
        "security": [
          {
            "apiKey": [
              "admin"
            ]
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AccountUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The account, as it is now.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Account"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_json`, `unknown_field` (of the body, or of `billing_address` or `tax_id`: `param` names which), `unknown_parameter`; `invalid_field`: `email` is not an address, `egress_overage_cap_bytes` not a whole number of bytes, `egress_overage` `stop` from an account that holds a balance, `billing_address` no address (of the US without its state and ZIP code, of Canada without its province or postal code), `tax_id` no `type` and `value`, or one of the two that Stripe takes not.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter",
              "invalid_field"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "description": "`key_scope`, `needs_api_key` (as everywhere); `needs_balance`: `egress_overage` `pay` from an account on Free or that holds no balance, which takes Paid and a top-up first; `account_held`: the account is held for its payments, and may read its account and change nothing of it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "key_scope",
              "needs_api_key",
              "needs_balance",
              "account_held"
            ]
          },
          "429": {
            "description": "`rate_limited` (`retryable`): too many links to verify an address of the account's notices for now (3, then one every ten minutes); nothing of the call is done.",
            "headers": {
              "Retry-After": {
                "required": true,
                "schema": {
                  "type": "string",
                  "pattern": "^[0-9]+$"
                },
                "description": "The seconds until one more try is taken."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "rate_limited"
            ]
          },
          "502": {
            "description": "`stripe_refused`: Stripe refused the billing address or the tax id for a reason that is not its form; it is not set (what the call gave before it may be). The message has Stripe's words and the id of its request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "stripe_refused"
            ]
          },
          "503": {
            "description": "`stripe_unavailable`: Stripe could not be reached to set the billing address or the tax id, which is not set (what the call gave before it may be): the same call again does it. `retryable`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "stripe_unavailable"
            ]
          }
        }
      }
    },
    "/v1/usage": {
      "get": {
        "operationId": "getUsage",
        "tags": [
          "Account"
        ],
        "summary": "What the sandboxes, volumes and snapshots of the key's project used and kept",
        "description": "The node's `GET /v1/usage` (`sandboxd/openapi/openapi.json`) as the control plane answers it. Given none of `from`, `to`, `group_by`, `metadata.<key>`, `kind`, `volume_id` and `snapshot_id`, it is the node's list of sandboxes (`UsageList`), asked of every node and put together. Given any of them, it is a report (`UsageReport`) from the books the control plane keeps by the hour, a `--usage-seconds` behind the nodes at most and there while a node is away: `prices` are the nine prices, every group and `totals` have the quantities only its books hold (`requests`, `state_gb_hours`, `storage_gb_hours`, `log_bytes`, `log_gb_hours`, `egress_eu_bytes`, `egress_ap_bytes`, `drive_write_bytes`, `drive_cold_read_bytes`, `browser_awake_seconds` and `cache_gb_hours`), and `bucket_seconds` is 3600. A report by sandbox has one more group, of what no one sandbox used (`sandbox_id` null): what volumes and snapshots kept and what was written to volumes and read of them cold, the requests apps took and what they printed, what the export of the apps' logs sent (`kind` `log_export`), what the backups of volumes kept in the object store (`kind` `backup`, in `state_gb_hours`), and what Browsers' profiles keep (`kind` `browser_profiles`, in `storage_gb_hours`). An account that owes may read it. Takes `read`.",
        "security": [
          {
            "apiKey": [
              "read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "description": "From this time on (RFC 3339): an hour is in the report when it begins at or after it.",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "to",
            "in": "query",
            "description": "Up to this time: an hour is in the report when it begins before it.",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "group_by",
            "in": "query",
            "description": "What the groups are of, separated by commas: `sandbox`, `hour` or `day`, `metadata.<key>`, and here `kind`, `volume` (or `drive`: a Drive is a volume, and the groups say `drive_id`), `snapshot` and `backup` (each backup of a volume a group of its own, `backup_id`). Without it a report is by sandbox, by volume when it asks of a volume, by snapshot when it asks of a snapshot, by backup when it asks of a backup (`backup_id`). Or `database` (each database of the project a group: what the instances and the Drives of its branches used, its history among them; `database_id`) or `branch` (each branch of a database a group, `database_id` and `branch_id`), with `hour` or `day` or alone, and nothing else (slice DB7): what no database's thing used is the group whose `database_id` is null.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "metadata.<key>",
            "in": "query",
            "description": "What was used under metadata with this value under this key, at the time it was used.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "sandbox_id",
            "in": "query",
            "description": "What one sandbox used: its id or its name (a deleted one by its id).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "kind",
            "in": "query",
            "description": "What one kind of thing used or kept: `sandbox`, `volume` (what volumes kept, and what was written to them and read of them cold), `drive` (the same, said of Drives: a Drive is a volume) or `snapshot`; `log_export`, what the nodes sent the destinations of the logs' export (`/v1/destinations`), which is booked under each destination (`dst-...`) and the app; or `backup`, what the backups of volumes kept in the object store (`/v1/backups`), which is booked under each backup (`bak-...`) on the meter of state (`state_gb_hours`): its `stored_bytes` from its `ready_at` to its delete, or its expiry where that came first, each hour to the hour it was kept in, up to the moment, the hour that has begun among it (booked every minute; one that is deleted, to its delete); or `browser_profiles`, what the Browsers' profiles of the project keep in the platform's store, which is booked under the bucket that holds them (`bkt-...`) as volume data (`storage_gb_hours`), each hour as the store says it. Another word is 400 `invalid_field`. The control plane's: a node that stands alone refuses it, as it does `volume_id` and `snapshot_id`.",
            "schema": {
              "type": "string",
              "enum": [
                "sandbox",
                "volume",
                "drive",
                "snapshot",
                "log_export",
                "backup",
                "browser_profiles"
              ]
            }
          },
          {
            "name": "volume_id",
            "in": "query",
            "description": "What one volume kept: its id or its name (a deleted one by its id). One of `sandbox_id`, `volume_id`, `drive_id`, `snapshot_id` and `backup_id` at a time.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "drive_id",
            "in": "query",
            "description": "What one Drive kept, was written and read cold: its id or its name (a deleted one by its id). A Drive is a volume: the same as `volume_id`, and the groups say `drive_id`. One of `sandbox_id`, `volume_id`, `drive_id` and `snapshot_id` at a time.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "snapshot_id",
            "in": "query",
            "description": "What one snapshot kept: its id or its name (a deleted one by its id); a snapshot of a Drive by its id (`dsnap-…`, slice D7): what it kept alone, on the meter of storage.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "backup_id",
            "in": "query",
            "description": "What one backup of a volume kept in the object store (`/v1/backups`): its id (`bak-…`), whatever became of its volume, on the meter of state (`state_gb_hours`). One of `sandbox_id`, `volume_id`, `drive_id`, `snapshot_id` and `backup_id` at a time; grouped by backup unless `group_by` says otherwise. The control plane's: a node that stands alone refuses it.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "How many rows or groups a page holds: 1 to 1000, 100 unless given.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 1000
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "The page after the one whose `next_cursor` this is.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The node's `UsageList`, or its `UsageReport` with the control plane's quantities in it (`sandboxd/openapi/openapi.json`).",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/UsageList"
                    },
                    {
                      "$ref": "#/components/schemas/UsageReport"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`; `invalid_field`: a time that is none, a range that ends before it begins, a `group_by` or `kind` that is none, two of `sandbox_id`, `volume_id` and `snapshot_id`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter",
              "invalid_field"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          },
          "429": {
            "description": "`report_in_progress`: another report of the same project was still being made after 2 seconds. `retryable`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "report_in_progress"
            ]
          },
          "503": {
            "description": "`report_too_long`: the report took more than 5 seconds to make; ask for shorter stretches and add them up, or for whole calendar months. `retryable`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "report_too_long"
            ]
          }
        }
      }
    },
    "/v1/billing": {
      "get": {
        "operationId": "getBilling",
        "tags": [
          "Billing"
        ],
        "summary": "The account's plan, what it has used of what the plan includes, and what it holds",
        "description": "The account of the key's project: the plan it is on and what that lets it have (the largest sandbox, how many apps); the stretch what the plan includes is reckoned over (`included_per`: Free's day, UTC, or a period of Paid's subscription), for each meter what that stretch includes, what the account's projects have used and have left of it, what is past it, at what price, what that comes to, and whether what the meter serves is stopped; what the stretch's meters come to at the prices; its balance, lot by lot with when each lapses, and what it owes; its subscription. Every project's keys see the same. Takes `read`.",
        "security": [
          {
            "apiKey": [
              "read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "The account's billing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Billing"
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          }
        }
      },
      "patch": {
        "operationId": "updateBilling",
        "tags": [
          "Billing"
        ],
        "summary": "Set the account's spend limit, or its plan",
        "description": "`spend_limit_usd`. The most the control plane takes from the account's card by itself in a calendar month (auto reload, and what is owed, on the billing day), for every month from now on; `null` takes the limit away. Auto reload makes no top-up with which what the card was charged by itself in the month (what failed, and what was taken back, left out) would come to more than the limit: with a limit of $45 and reloads of $20 there are two in a month. The charge of what an account in debt owes, on its billing day, is no more than what the limit leaves, and auto reload counts it in turn. A top-up the account asks for itself is not counted, and is not held back. The limit stops nothing by itself. A limit that is raised, or taken away, lets auto reload make its top-up with this call. For an account that has never topped up there is no auto reload, and the limit holds nothing. An account that never chose one has its plan's (`spend_limit_usd` of `GET /internal/control` `plans`: $50 on Paid, none on Free, as the control plane is started by default).\n\n`plan`. `paid`: the account subscribes. A charge of Paid's fee, $5, is made (`GET /v1/billing/charges`, `kind`: `subscription`), and the account is on Paid, in a period with Paid's quantities whole, from the moment it is collected; the same call again makes no second charge. `free` ends the subscription at the end of the period that is paid for; `paid` asked for before that end takes the ending back. An account that is no business, where only businesses are sold to, may not ask for Paid: 403 `tax_region_business_only`, before anything of the call is done (control/README.md, 税). 409 `subscription_charge_pending`: `free` while a charge of the subscription (its first period's, or its next one's) is being collected, which cannot be taken back and, collected, would begin the period the account says it does not want.\n\nTakes `admin`.",
        "security": [
          {
            "apiKey": [
              "admin"
            ]
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BillingUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The account's billing, as it is now.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Billing"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_json`, `unknown_field`, `unknown_parameter`; `invalid_field`: `spend_limit_usd` is not a number of dollars, zero or more, or null; `plan` is not one of the two plans (`free`, `paid`); `start_now_required`: the account buys as a consumer in the EEA or the UK and did not ask for what it buys to be provided at once (`start_now`), and nothing was made.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter",
              "invalid_field",
              "start_now_required"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/TrustLimitTaxRegionOrPlan"
          },
          "409": {
            "description": "`subscription_charge_pending`: a charge of the account's subscription is being collected, and what it pays for cannot be changed, nor the subscription ended, until it has an answer. `retryable`. Of Paid, `billing_address_required` (`param` `billing_address`): the platform computes tax with Stripe Tax and Stripe collects, and Stripe Tax has no place for the account: it has no billing address, and the country of its card on record is not known (set its address with `PATCH /v1/account`, or pay on a payment page, `POST /v1/billing/checkout`, which asks for it). Nothing was made.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "subscription_charge_pending",
              "billing_address_required"
            ]
          }
        }
      }
    },
    "/v1/billing/statement": {
      "get": {
        "operationId": "getBillingStatement",
        "tags": [
          "Billing"
        ],
        "summary": "A month's statement",
        "description": "A calendar month (UTC), whatever the periods of the account's subscription: the parts it is made of (the periods that have hours in it, and the hours in no period, which are Free's, priced at nothing), each with, for every meter, what was used in it, what its period includes, what was past that, the price and the amount, and what the lines come to; each meter's line for the whole month; what the parts add up to, which is what the month's usage takes from the account's balance as far as the balance goes; the fees of the periods that began in the month; which Credits paid, by the kind of lot, what none has paid and is owed, and what none could pay and is written off; what was added to the balance in the month and what lapsed with it; what each project used, to be held against `GET /v1/usage` (`from` and `to` the month's) with that project's key; and what the account held at the month's end. A period that lies in two months has what it owes split between their two statements so that the two add up to it, to the millionth of a dollar. The month that is not over yet is as it stands. A month that is over does not change in what was used and what it came to, unless usage of it comes in afterwards (a node that was away): then it is not `closed` until what it comes to is taken from the balance, and is closed again within seconds. What it says paid for it changes when something it owed is paid by Credits that came later. A month the account had nothing in has nothing used. Takes `read`.",
        "security": [
          {
            "apiKey": [
              "read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "period",
            "in": "query",
            "description": "The month, `YYYY-MM`, one that has begun. By default the month it is.",
            "schema": {
              "type": "string",
              "pattern": "^[0-9]{4}-[0-9]{2}$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The statement.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BillingStatement"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_field` (`period`: not a month, or one that has not begun), `unknown_parameter`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_field",
              "unknown_parameter"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          }
        }
      }
    },
    "/v1/apps": {
      "post": {
        "operationId": "createApp",
        "tags": [
          "Apps"
        ],
        "summary": "Make an app, and deploy its first version when it is given an image",
        "description": "Every field may be left out. With an `image` the app's first version is made with it and deployed: `deploying` is that version, and `current_version` is it once it is ready. Without one the app has no version, and takes no traffic. Takes `write`; an app that is given secrets takes `admin` besides.",
        "security": [
          {
            "apiKey": [
              "write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AppCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The app.",
            "headers": {
              "Location": {
                "schema": {
                  "type": "string"
                },
                "description": "`/v1/apps/{id}`."
              },
              "Idempotent-Replayed": {
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                },
                "description": "Present when the answer is the app an earlier request with the same `Idempotency-Key` made, as it is now."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/App"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/AppRefused"
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "$ref": "#/components/responses/AppNotAllowed"
          },
          "404": {
            "description": "`secret_not_found`: the project has no secret of a name `secrets` gives (`param` is `secrets.<variable>`), of the name `image_pull_secret` gives (`param` is `image_pull_secret`), of one a file holds (`param` is `files[<n>].secret`), or of one a reference of `env` leads to (`param` is `env.<variable>`); `volume_not_found`: the project has no volume of a name or id `volumes` gives (`param` is `volumes[<n>].volume`); `app_not_found`: the app this `Idempotency-Key` made has been deleted since. `drive_not_found`: `drives` names a Drive the project does not have, or a volume of another kind (`param` `drives[i].drive`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "secret_not_found",
              "volume_not_found",
              "app_not_found",
              "drive_not_found"
            ]
          },
          "409": {
            "description": "`name_taken`: the project has an app of the name, or the name is another app's alias (`param` is `name`), or an alias is another app's name or alias (`param` is `network.aliases[<n>]`); `volumes_apart`: the service's volumes of its own are on different machines, and its one instance has them all (`param` is `volumes`); `idempotency_key_reused`. `drive_single_writer`: `drives` of an app of many instances, or of keys: a Drive has one writer (`param` names what makes it many; an app's version mounts no snapshot of a Drive read-only yet); `too_many_drives`: more than 8 Drives and volumes of its own an instance would mount; `key_drive`: `drives` names a key's Drive. `managed_by_database`: `drives` (or `volumes`) names a database's Drive, which its instance alone mounts (`param` names it).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "name_taken",
              "volumes_apart",
              "idempotency_key_reused",
              "drive_single_writer",
              "too_many_drives",
              "key_drive",
              "managed_by_database",
              "managed_by_installation"
            ]
          }
        }
      },
      "get": {
        "operationId": "listApps",
        "tags": [
          "Apps"
        ],
        "summary": "The project's apps",
        "description": "The newest first. Takes `read`.",
        "security": [
          {
            "apiKey": [
              "read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of apps.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AppList"
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`, `invalid_field` (`limit`), `invalid_cursor`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter",
              "invalid_field",
              "invalid_cursor"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          }
        }
      }
    },
    "/v1/billing/balance": {
      "get": {
        "operationId": "getBalance",
        "tags": [
          "Billing"
        ],
        "summary": "The Credits the account holds, and how it is reloaded",
        "description": "The account of the key's project: what it holds and what it owes, the lots that is in (the kind of each, what is left of it and when it lapses, the one that lapses first first), its auto reload, and what auto reload has taken this month against the account's spend limit. Every project's keys see the same. Takes `read`.",
        "security": [
          {
            "apiKey": [
              "read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "The balance.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Balance"
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          }
        }
      },
      "patch": {
        "operationId": "updateBalance",
        "tags": [
          "Billing"
        ],
        "summary": "Set the account's auto reload",
        "description": "Whether auto reload is on, the line below which the balance is topped up, and the amount of a top-up: each of the three that is given, for the account. An account that has set none of them has them as the control plane was started (auto reload off, below 5 Credits, $20, by default). Any account turns it on and off, on either plan, whether it ever topped up or not (on Free it reloads nothing: Free is never charged); turning it on takes a card on record (`Balance.card`), and gives an account that never chose a spend limit, and has none, the one its plan gives an account when it first pays (`spend_limit_usd` of the plan's flag: $50 on Paid as the control plane is started by default; Free gives none, and such an account on it is given what its trust level lets auto reload take in a month), no more than its trust level lets auto reload take. The account is looked at with this call: a top-up that is due by what was set is made before the answer. Setting any of them, or nothing, is a new start for an auto reload that has failed and is no longer tried by itself. What auto reload may take in a month is the account's spend limit (`PATCH /v1/billing`). Takes `admin`.",
        "security": [
          {
            "apiKey": [
              "admin"
            ]
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BalanceUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The balance, with the auto reload as it is now.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Balance"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_json`, `unknown_field`, `unknown_parameter`; `invalid_field`: `auto_reload` is not true or false, `reload_below_credits` is not a number of Credits, zero or more, `reload_amount_usd` is not a number of dollars in whole cents, or is less than the smallest top-up; `start_now_required`: auto reload is turned on by an account that buys as a consumer in the EEA or the UK, without the request its reloads carry (`start_now`), and nothing was changed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter",
              "invalid_field",
              "start_now_required"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/TrustLimit"
          },
          "409": {
            "description": "`no_card`: `auto_reload` is true, Stripe collects the charges, and the account has no card on record. Nothing was changed. With `auto_reload` true, `billing_address_required` (`param` `billing_address`): the platform computes tax with Stripe Tax and Stripe collects, and Stripe Tax has no place for the account: it has no billing address, and the country of its card on record is not known (set its address with `PATCH /v1/account`, or pay on a payment page, `POST /v1/billing/checkout`, which asks for it). Nothing was made.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "no_card",
              "billing_address_required"
            ]
          }
        }
      }
    },
    "/v1/apps/{app}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/App"
        }
      ],
      "get": {
        "operationId": "getApp",
        "tags": [
          "Apps"
        ],
        "summary": "One app",
        "security": [
          {
            "apiKey": [
              "read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "The app as it is now.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/App"
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          },
          "404": {
            "$ref": "#/components/responses/NoSuchApp"
          }
        }
      },
      "patch": {
        "operationId": "updateApp",
        "tags": [
          "Apps"
        ],
        "summary": "Change an app",
        "description": "A JSON Merge Patch. A change of what a version is made of (`image`, `start_command`, `port`, `ready`, `env`, `secrets`, `size`) deploys a new version of an app that has an image; a change of the rest (`name`, `regions`, `scale`, `keys`, `egress`, `metadata`) holds at once and makes none: the nodes that serve the app are told of its scale and its labels. `null` takes a field back to what it is when nothing is said; `image` and `name` cannot be taken away. A patch that changes nothing writes nothing. Takes `write`; giving the app a secret it does not have takes `admin` besides. Of the project's default sandbox app (`sbx-sandbox`, `managed_by` of the Sandboxes product) the patch takes `image` alone, a reference or null for the base template: a new version made of it (answered 200 with the app, `current_version` the new one; the same image again is none), of which the project's creates that name no image are made from then on; anything else is 409 `managed_by_sandboxes`, as every change of an image's app is.",
        "security": [
          {
            "apiKey": [
              "write"
            ]
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AppUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The app after the change, and `made_version`: the version the change made, `null` if it made none.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AppChanged"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/AppRefused"
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "$ref": "#/components/responses/AppNotAllowed"
          },
          "404": {
            "description": "`app_not_found`; `secret_not_found`; `volume_not_found`. `drive_not_found`: `drives` names a Drive the project does not have, or a volume of another kind (`param` `drives[i].drive`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "app_not_found",
              "secret_not_found",
              "volume_not_found",
              "drive_not_found"
            ]
          },
          "409": {
            "description": "`name_taken`: another app has the name, or has it as an alias, or has an alias as a name or an alias (`param` is `name` or `network.aliases[<n>]`); `volumes_apart`; `app_changed`: other calls changed the app while this one was worked out, again and again (`retryable`). `drive_single_writer`: `drives` of an app of many instances, or of keys: a Drive has one writer (`param` names what makes it many; an app's version mounts no snapshot of a Drive read-only yet); `too_many_drives`: more than 8 Drives and volumes of its own an instance would mount; `key_drive`: `drives` names a key's Drive. `managed_by_database`: the app is a database's (`managed_by`), changed through `/v1/databases` alone. `managed_by_sandboxes`: the app is the Sandboxes product's (`managed_by`), whose instances are the project's sandboxes, made, changed and deleted through `/v1/sandboxes`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "name_taken",
              "volumes_apart",
              "app_changed",
              "drive_single_writer",
              "too_many_drives",
              "key_drive",
              "managed_by_database",
              "managed_by_installation",
              "managed_by_sandboxes"
            ]
          }
        }
      },
      "delete": {
        "operationId": "deleteApp",
        "tags": [
          "Apps"
        ],
        "summary": "Delete an app",
        "description": "The app is no more and its name is free. The nodes that serve it are told: they give it no request from then on, and its instances are deleted. One with nothing in hand is deleted at once. What the others have in hand is not waited for as it is when a deploy takes a version off the nodes: the upgraded connections (WebSockets) are closed at once, the requests are given 30 seconds to end (a node's `--app-delete-grace-seconds`) and are cut off then, and the instances are deleted. The templates of its versions are taken off the nodes. Takes `write`.",
        "security": [
          {
            "apiKey": [
              "write"
            ]
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted."
          },
          "400": {
            "description": "`unknown_parameter`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          },
          "404": {
            "$ref": "#/components/responses/NoSuchApp"
          },
          "409": {
            "description": "`managed_by_database`: the app is a database's (`managed_by`), changed through `/v1/databases` alone.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "managed_by_database",
              "managed_by_installation"
            ]
          }
        }
      }
    },
    "/v1/apps/{app}/versions": {
      "parameters": [
        {
          "$ref": "#/components/parameters/App"
        }
      ],
      "post": {
        "operationId": "deployApp",
        "tags": [
          "Apps"
        ],
        "summary": "Deploy: make a version of an app",
        "description": "A version is made every time, of what the call gives and, for what it leaves out, of what the app has; the app's own fields become what the version is made of. The same tag deployed again is another version: the tag may name another image by then. The version is built on the app's nodes and takes the app's traffic when every one of them has it (`GET` it with `wait_seconds` to wait for that). Takes `write`; giving the app a secret it does not have takes `admin` besides.",
        "security": [
          {
            "apiKey": [
              "write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Deploy"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The version, `building`.",
            "headers": {
              "Location": {
                "schema": {
                  "type": "string"
                },
                "description": "`/v1/apps/{app id}/versions/{id}`."
              },
              "Idempotent-Replayed": {
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                },
                "description": "Present when the answer is the version an earlier request with the same `Idempotency-Key` made, as it is now."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Version"
                }
              }
            }
          },
          "400": {
            "description": "As for a change of an app; and `missing_field`: the app has no image and the call gives none.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter",
              "invalid_field",
              "invalid_image",
              "invalid_env",
              "missing_field",
              "invalid_idempotency_key",
              "volume_single_instance"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "$ref": "#/components/responses/AppNotAllowed"
          },
          "404": {
            "description": "`app_not_found`; `secret_not_found`; `volume_not_found`. `drive_not_found`: `drives` names a Drive the project does not have, or a volume of another kind (`param` `drives[i].drive`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "app_not_found",
              "secret_not_found",
              "volume_not_found",
              "drive_not_found"
            ]
          },
          "409": {
            "description": "`idempotency_key_reused`; `volumes_apart`; `app_changed` (`retryable`). `drive_single_writer`: `drives` of an app of many instances, or of keys: a Drive has one writer (`param` names what makes it many; an app's version mounts no snapshot of a Drive read-only yet); `too_many_drives`: more than 8 Drives and volumes of its own an instance would mount; `key_drive`: `drives` names a key's Drive. `managed_by_database`: the app is a database's (`managed_by`), changed through `/v1/databases` alone. `managed_by_sandboxes`: the app is the Sandboxes product's (`managed_by`), whose instances are the project's sandboxes, made, changed and deleted through `/v1/sandboxes`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "idempotency_key_reused",
              "volumes_apart",
              "app_changed",
              "drive_single_writer",
              "too_many_drives",
              "key_drive",
              "managed_by_database",
              "managed_by_installation",
              "managed_by_sandboxes"
            ]
          }
        }
      },
      "get": {
        "operationId": "listVersions",
        "tags": [
          "Apps"
        ],
        "summary": "An app's versions",
        "description": "The newest first. Takes `read`.",
        "security": [
          {
            "apiKey": [
              "read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of versions.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VersionList"
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`, `invalid_field`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter",
              "invalid_field"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          },
          "404": {
            "$ref": "#/components/responses/NoSuchApp"
          }
        }
      }
    },
    "/v1/apps/{app}/versions/{version}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/App"
        },
        {
          "name": "version",
          "in": "path",
          "required": true,
          "description": "The version's id (`ver-...`) or its number (1 for the app's first).",
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "operationId": "getVersion",
        "tags": [
          "Apps"
        ],
        "summary": "One version",
        "description": "With `wait_seconds` the answer comes when the version is no longer `building`, or when the time is up: the version as it is then. Takes `read`.",
        "security": [
          {
            "apiKey": [
              "read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "wait_seconds",
            "in": "query",
            "description": "For how long to wait for the version's build to end.",
            "schema": {
              "type": "number",
              "minimum": 0,
              "maximum": 3600
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The version.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Version"
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`, `invalid_field`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter",
              "invalid_field"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          },
          "404": {
            "description": "`app_not_found`; `version_not_found`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "app_not_found",
              "version_not_found"
            ]
          }
        }
      }
    },
    "/v1/apps/{app}/rollback": {
      "parameters": [
        {
          "$ref": "#/components/parameters/App"
        }
      ],
      "post": {
        "operationId": "rollbackApp",
        "tags": [
          "Apps"
        ],
        "summary": "Send an app's traffic to one of its versions that is ready",
        "description": "Where a node has the version's template, the traffic goes to the version from the moment the call is answered, and nothing is built: the nodes that have the template are told of the version before the traffic moves, and the version the traffic leaves is taken off them after. An earlier version or a later one. A deploy that was going on ends: its version takes no traffic when it is ready.\n\nWhere no node has the version's template (it was taken off the nodes when the app had newer versions to go back to: the version's `collected_at`), the version is built again first, as a deploy builds one: with the image it was resolved to when it was first deployed, and the project's secrets as they are now. The call is answered 202 at once; the version is `building`, the app is deploying it (`deploying`), and the traffic stays where it is until every node has the template, and for good if the version cannot be built again: it is then `failed` with why (`app.deploy_failed`), and a rollback to it may be asked for again. `GET /v1/apps/{app}/versions/{version}?wait_seconds=` waits for it as for a deploy, and `app.deployed` says when the traffic has moved. Takes `write`.",
        "security": [
          {
            "apiKey": [
              "write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Rollback"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The app, with `current_version` the version named and `deploying` null.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/App"
                }
              }
            }
          },
          "202": {
            "description": "The version is being built again first, no node having its template: the app, with `deploying` the version named and `current_version` what it was.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/App"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_json`, `unknown_field`, `unknown_parameter`, `invalid_field`; `missing_field`: no `version`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter",
              "invalid_field",
              "missing_field",
              "invalid_idempotency_key"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          },
          "404": {
            "description": "`app_not_found`; `version_not_found` (`param` is `version`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "app_not_found",
              "version_not_found"
            ]
          },
          "409": {
            "description": "`version_not_ready`: the version is being built, or failed and never was ready; `rollback_failed`: the version is of a service with a volume of its own, whose instance of it did not come up (the message says what it said): the service has its instance of the version the traffic was with again, with the volume, and the traffic stays there (`param` is `version`); `idempotency_key_reused`. `managed_by_database`: the app is a database's (`managed_by`), changed through `/v1/databases` alone. `managed_by_sandboxes`: the app is the Sandboxes product's (`managed_by`), whose instances are the project's sandboxes, made, changed and deleted through `/v1/sandboxes`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "version_not_ready",
              "rollback_failed",
              "idempotency_key_reused",
              "managed_by_database",
              "managed_by_installation",
              "managed_by_sandboxes"
            ]
          }
        }
      }
    },
    "/v1/apps/{app}/logs": {
      "parameters": [
        {
          "$ref": "#/components/parameters/App"
        }
      ],
      "get": {
        "operationId": "readAppLog",
        "tags": [
          "Apps"
        ],
        "summary": "An app's log: what its instances printed, a line for each request, what the platform did",
        "description": "The lines of every instance of the app, on every machine that serves it, as one sequence. Takes `read`.\n\nThree kinds of lines. `output`: a line that an instance's start command printed, on its standard output or its standard error. `request`: one for each request that was given to an instance, written when its answer has ended, and one for each that the machine it came to refused. `platform`: what the platform did (an instance made, stopped, retired, deleted, each with why; a version served by a machine, and taken off it), what the log dropped or lost, and what this call has to say of the machines it reads.\n\nWhere the lines are. Each machine that serves the app keeps the lines of its own instances and ships them to the object store as they come, in pieces sealed with a key of the project's own (`POST /internal/logs/shipped` in `sandboxd/openapi/openapi.json`); it lets go of no line it has not shipped. A reading takes what the store holds first and then what the machines hold after it, machine by machine, and puts it all into one: every line of the days the account's plan keeps (3 days on Free, 30 on Paid, as the control plane is started unless told otherwise; `kept_until` of `GET /v1/apps/{app}/logs/usage`), of the machines that are there and of those that are gone, of instances that run and of those deleted long ago. What a deleted instance printed last is read through this call like anything else, with a last line that says it was deleted and why. Of its own, a machine keeps of an instance that is there the last megabytes it printed, and of one that was deleted its last megabyte for seven days (the numbers are `app_logs` of a machine's `GET /internal/node`, which also says what a machine cuts off a line and drops of an instance that prints faster than it keeps, each said in the log); what it has shipped is in the store besides.\n\nLines that are missing. A machine that is gone from the books, or that lost what it kept of the app, before it had shipped its last lines leaves them missing for good, and a line says so where they would be (`logs.gap`, with `reason` `node_gone` or `log_gone`, `from`, `to`, and how many lines at the most); so does a piece of the store that cannot be read (`unreadable`). These are sent whatever the reading asks for, but for its times. A machine that could not ship for a while (the store out of reach) holds its lines until it has all it keeps of one app, and then lets go of the oldest, which its own line says (`logs.gap`, `node_buffer`: a line of the log like any other, once a second at the most) and the counts too (`not_kept.node_buffer` of the usage).\n\nWhat a reading reads. One reading reads at most 256 MiB of lines from the store, and for 10 seconds at the most (as the control plane is started unless told otherwise: `--log-read-mib`, `--log-read-seconds`): where it has read that much, it ends, following or not, with a mark that says there is more (`more`), and the reader goes on with its cursor. The lines it asks for by what they say (`status`, `method`, `path`, `request_id`, `event`, `stream`, `contains`) are looked for in all that it reads, from the store and from the machines.\n\nOrder. A machine's lines are in that machine's order: by their times, a request's line after what its instance printed for it, to within the few milliseconds an instance's clock and its machine's agree to (a machine sets its instances' clocks to within 5 ms of its own): each line is stamped where it is taken, an output line by the instance's clock as its agent takes it from the command, a request's by the machine's as its answer ends. Between machines the line with the earlier time goes first, each time by its own machine's clock: the lines of two machines whose clocks differ are interleaved that much off, and no time is changed. A line is not sent while another machine may still have an earlier one; it waits for that for 0.4 seconds at the most in an answer that follows, and for 5 seconds in one that does not. Two things come with times before those of the lines they follow: what the start command printed while a machine built the version's template (`from`: `template`; once for each machine), and `template.built`.\n\nThe cursor. Every line carries `cursor`: where the reader stands after it, on every machine it has lines of. A reader that comes back with it (the parameter `cursor`; a stream of events sends the id of its last event by itself, as `Last-Event-ID`) gets every later line once, none twice and none left out. A break costs the reading again of what was on its way. A cursor holds for as long as the machines keep the lines after it: what a machine has let go of since is not in the answer.\n\nA machine that cannot be reached. Its lines are missing from the answer, and a line says so (`logs.incomplete`), once: of a machine that serves a version of the app, of one the reader's cursor has a position on, and of one this call was reading when it went. The other machines' lines come as they do. A reader that follows is told when the machine is reached again (`logs.complete`) and then gets what the machine kept in between, with the times those lines have; one that does not follow gets them with its next call, by its cursor. `logs.anew`: a machine keeps the app's log anew, what it kept before is gone, and its log is read from the beginning.\n\nThe answer is lines of JSON, each one line of the log (`application/x-ndjson`), or, where the request says `Accept: text/event-stream`, a stream of events: the event's name is the line's kind, its `id` the cursor, and its data the line without `cursor`. `mark` is no line of the log: it says where the reader stands when there is nothing to send, at the end of an answer that does not follow and every fifteen seconds of silence in one that does. An answer that does not follow ends when every machine's has. One that follows goes on until the reader leaves, and one that follows up to a time (`until`) until two seconds after that time.\n\nNothing of an app's environment is in a line the platform writes: a request's line has the path without its query and no header but the request's id. What a start command prints is the app's own, in the log as it was printed, and read by the app's project alone: to a key of another project there is no such app, and the pieces in the store are sealed with a key no other project's lines are. The log of an app that was deleted is read by the app's id, not by its name, for as long as its account's plan keeps the lines; its owner deletes the lines before a time with `DELETE /v1/apps/{app}/logs`.",
        "security": [
          {
            "apiKey": [
              "read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "since",
            "in": "query",
            "description": "Lines of this time or later.",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "until",
            "in": "query",
            "description": "Lines before this time.",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "instance",
            "in": "query",
            "description": "One instance's lines, by its id (`sbx-...`).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "version",
            "in": "query",
            "description": "One version's lines: its id (`ver-...`) or its number.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "kinds",
            "in": "query",
            "description": "Which kinds of lines: some of `output`, `requests` and `platform`, with commas between them. Left out: all. What this call says of the machines it reads (`logs.incomplete` and the like) is sent whatever is asked for.",
            "schema": {
              "type": "string",
              "pattern": "^(output|requests|platform)(,(output|requests|platform))*$"
            }
          },
          {
            "name": "follow",
            "in": "query",
            "description": "`true`: the answer goes on with the lines that come, each within a second of when it was written, also of instances that are made after the call.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "The `cursor` of the last line the reader has: the answer begins after it.",
            "schema": {
              "type": "string",
              "pattern": "^lc1-[A-Za-z0-9_-]*$"
            }
          },
          {
            "name": "status",
            "in": "query",
            "description": "Request lines whose `status` is this (`500`), in this hundred (`5xx`), at least this (`>=400`), or below it (`<300`).",
            "schema": {
              "type": "string",
              "pattern": "^([1-9][0-9][0-9]|[1-9]xx|>=[1-9][0-9][0-9]|<[1-9][0-9][0-9])$"
            }
          },
          {
            "name": "method",
            "in": "query",
            "description": "Request lines of this method, in capitals or not.",
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 16
            }
          },
          {
            "name": "path",
            "in": "query",
            "description": "Request lines whose `path` begins with this.",
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 1024
            }
          },
          {
            "name": "request_id",
            "in": "query",
            "description": "The line of the request of this id (`request_id`).",
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 64
            }
          },
          {
            "name": "event",
            "in": "query",
            "description": "Platform lines of this `event`.",
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 64
            }
          },
          {
            "name": "stream",
            "in": "query",
            "description": "Output lines of this stream.",
            "schema": {
              "type": "string",
              "enum": [
                "stdout",
                "stderr"
              ]
            }
          },
          {
            "name": "contains",
            "in": "query",
            "description": "Lines whose `text` or `message` has this in it, as it is given (capitals count).",
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 1024
            }
          },
          {
            "name": "Last-Event-ID",
            "in": "header",
            "description": "For a stream of events: the id of the last event the reader has, which is a cursor. `cursor` in the query goes before it.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The lines. What goes wrong after the answer has begun is said in it (`logs.incomplete`), or ends it.",
            "content": {
              "application/x-ndjson": {
                "schema": {
                  "$ref": "#/components/schemas/LogLine"
                }
              },
              "text/event-stream": {
                "schema": {
                  "type": "string",
                  "description": "Events `output`, `request`, `platform` and `mark`: `id` is the cursor, the data a `LogLine` without `cursor` (of `mark`: `{\"cursor\": ...}`)."
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`; `invalid_field`: `since` or `until` is no time, `instance` is no id, `kinds` names a kind there is none of, `follow` is neither `true` nor `false`, `status` is none of the forms it takes, `stream` is neither `stdout` nor `stderr`, `method`, `path`, `request_id`, `event` or `contains` is empty or longer than it may be; `invalid_cursor`: not a cursor this call handed out.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter",
              "invalid_field",
              "invalid_cursor"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          },
          "404": {
            "description": "`app_not_found` (an app that was deleted is found by its id, for as long as its lines are kept); `version_not_found` (the app has no such version).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "app_not_found",
              "version_not_found"
            ]
          },
          "429": {
            "description": "`too_many_followers`: as many readers follow the logs of the account's apps, in all its projects, as its plan lets follow at a time (`logs.followers` of `GET /v1/account`); `retryable`. A reading that does not follow is never refused so.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "too_many_followers"
            ]
          }
        }
      },
      "delete": {
        "operationId": "deleteAppLog",
        "tags": [
          "Apps"
        ],
        "summary": "Delete an app's log before a time",
        "description": "The app's owner deletes the lines of its log before a time (`before`; now, when it is left out). From this call on no reading is given one of them, from the store or from a machine; the pieces in the object store whose every line is before it go now, and a piece that has later lines too goes when those have had their days (a piece goes whole). What a machine holds of them on its own disk goes as a machine's lines go, unread. A time earlier than one the app's log was deleted before deletes nothing more. For an app that was deleted, by its id, for as long as its lines are kept. Takes `write`.",
        "security": [
          {
            "apiKey": [
              "write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "before",
            "in": "query",
            "description": "The lines of an earlier time than this go. Left out: now.",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "No reading is given a line before `before` from here on; what goes of the store now.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AppLogDeletion"
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`; `invalid_field`: `before` is no time.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter",
              "invalid_field"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          },
          "404": {
            "description": "`app_not_found` (an app that was deleted is found by its id, for as long as its lines are kept).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "app_not_found"
            ]
          }
        }
      }
    },
    "/v1/apps/{app}/logs/usage": {
      "parameters": [
        {
          "$ref": "#/components/parameters/App"
        }
      ],
      "get": {
        "operationId": "getAppLogUsage",
        "tags": [
          "Apps"
        ],
        "summary": "What an app's log counted, day by day or hour by hour",
        "description": "What every machine that keeps lines of the app counted of its log, added up: of what its instances printed, the lines and bytes printed, kept, not kept and why (`rate_limit`: dropped, beyond the lines an instance has kept for one second, by the bytes they were printed as; `keep_off`: in the log on the machine and not kept for good, the app's `logs.keep` being false at their time), cut and the bytes cut off them, and lost before the machine read it; of its requests, those handled, the lines kept and the requests they stand for (a line of refusals says how many in `times`), and those left out for the app's sample (`logs.requests.sample_rate`); the platform's lines; of each kind, the lines a machine let go of before it had shipped them (`node_buffer`), and of output the lines at the most that machines which went had not shipped (`node_lost`). The bytes of a line kept are those of its `text`, as a reader of the log is given it: added up over what is read of a day, they are that day's `output.kept.bytes`. What was printed is what was kept, not kept and cut off, to the line and to the byte. And what the edges refused of the app's requests themselves, which no machine saw and no line of the log has (`edge_refused`, by the error code: an app that has no version ready, `app_not_deployed`; a project that is stopped; an edge's own limit of connections). Takes `read`.\n\nEach machine counts its log of the app by the hour, as it keeps the lines, and the control plane takes its counts in with what it asks of the machine every few seconds. A day (or an hour) is `complete` when every machine that keeps lines of the app has had its counts taken in after the day was over and settled (ten seconds after its end), or, for the day that is not over, is there and had them taken in a moment ago. A machine that cannot be reached makes the days it has not been taken in past not complete, until it is back. The counts are kept for 92 days.",
        "security": [
          {
            "apiKey": [
              "read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "description": "The first day (or hour) is the one this time is in. Left out: six days before the last (23 hours, by the hour).",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "to",
            "in": "query",
            "description": "The days (or hours) up to the one this time is in, and not past it: one that begins at it is not among them. Left out: up to the end of today (of this hour).",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "by",
            "in": "query",
            "description": "`day` (left out) or `hour`. At most 366 days, or 744 hours.",
            "schema": {
              "type": "string",
              "enum": [
                "day",
                "hour"
              ],
              "default": "day"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Every day (or hour) asked for, the earliest first, with every count, those that are none too.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AppLogUsage"
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`; `invalid_field`: `from` or `to` is no time, `to` is not later than `from`, the stretch is longer than 366 days (744 hours), `by` is neither `day` nor `hour`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter",
              "invalid_field"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          },
          "404": {
            "description": "`app_not_found` (an app that was deleted is found by its id, for as long as its lines are kept).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "app_not_found"
            ]
          }
        }
      }
    },
    "/v1/apps/{app}/metrics": {
      "parameters": [
        {
          "$ref": "#/components/parameters/App"
        }
      ],
      "get": {
        "operationId": "getAppMetrics",
        "tags": [
          "Apps"
        ],
        "summary": "An app's metrics, minute by minute or hour by hour",
        "description": "What every node that serves the app counted of it, by the minute, added up step by step: its requests (every one, and those a node refused, `refused`, which no usage counts), by the class of their status; how long they took, the p50 and the p99 of the buckets of all of them (OpenTelemetry's for HTTP); its instances awake (running, being brought up or made), stopped (paused or asleep) and in all, and the requests they had in hand; its wakes and the instances made; the CPU time and memory its instances used (memory as an average over the step, in the steps of 128 MiB the usage counts). A step with nothing in it has every count none. Takes `read`.\n\nEach node counts its apps' minutes in memory and gives them to the control plane with what it asks of the node every few seconds; a node that restarts begins every count anew. The minutes are kept for 24 hours, then by the hour for 30 days.",
        "security": [
          {
            "apiKey": [
              "read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "description": "The first step is the one this time is in. Left out: an hour before `to` (24 hours, by the hour). Minutes are kept for the last 24 hours.",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "to",
            "in": "query",
            "description": "The steps up to the one this time is in, and not past it. Left out: up to the end of this minute (of this hour).",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "step",
            "in": "query",
            "description": "`minute` (left out) or `hour`. At most 1,440 minutes, or 720 hours.",
            "schema": {
              "type": "string",
              "enum": [
                "minute",
                "hour"
              ],
              "default": "minute"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Every step asked for, the earliest first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AppMetrics"
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`; `invalid_field`: `from` or `to` is no time, `to` is not later than `from`, more steps than are given, minutes older than a day, `step` is neither `minute` nor `hour`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter",
              "invalid_field"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          },
          "404": {
            "description": "`app_not_found` (an app that was deleted is found by its id, for as long as its log is kept).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "app_not_found"
            ]
          }
        }
      }
    },
    "/v1/destinations": {
      "post": {
        "operationId": "createDestination",
        "tags": [
          "Destinations"
        ],
        "summary": "Export the logs of the project's apps to an OpenTelemetry endpoint",
        "description": "Every node that keeps a log of one of the apps the destination takes sends it the lines of that log it takes, as OTLP over HTTP (its exporter, `sandboxd export`), from the moment it is connected. One record is sent to it now, from the control plane: a line of the platform's that says it is connected (`sbx.destination.connected`, `log.record.uid` `<id>.try-<milliseconds>`). An answer of 2xx that refuses no record connects it (`status.connected`); any other leaves it unconnected and sent nothing, and `status.tried` has what the endpoint said (512 bytes at the most, every header's value cut out first). `POST /v1/destinations/{destination}/test` tries it again. Its host has to resolve to an address on the internet, the connection is made to the address that was checked, and a redirect is not followed. What the nodes send it is the account's traffic to the internet: booked to the project's hours under the destination and the app (`GET /v1/usage` with `kind=log_export`), so what the plan includes covers it and past that it is priced as all traffic is; while the account's traffic is stopped nothing is sent, and a line that waits for it longer than an exporter holds one (ten minutes, or past the last 64 MiB of a log it has not taken) is not sent: the log says so (`export.dropped`), and so does what was exported of it (`GET /v1/apps/{app}/logs/usage`, `exported`). An account has as many destinations in all its projects as its plan lets it (`destinations=` of the plan's flag: 1, 3 and 10 unless the control plane is told otherwise). The headers' values are credentials: kept sealed, given to the nodes, and in no answer. Takes `admin`.",
        "security": [
          {
            "apiKey": [
              "admin"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DestinationCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The destination, with what its try came to (`status.tried`, and `status.connected`).",
            "headers": {
              "Idempotent-Replayed": {
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                },
                "description": "Present when the answer is the one of an earlier request with the same `Idempotency-Key`."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Destination"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_json`, `unknown_field`, `unknown_parameter`; `missing_field`: no `name`, or neither `endpoint` nor `logs_endpoint`; `invalid_name`: a name that is none a destination may have; `invalid_field`: an address that is not `https://` (or has a user in it, or is longer than 2,048 bytes), a header a destination may not have or a value that is not one line of text, an attribute the platform sets, a `protocol`, `compression`, `kinds`, `signals` or `type` that is none; `invalid_idempotency_key`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter",
              "missing_field",
              "invalid_name",
              "invalid_field",
              "invalid_idempotency_key"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "description": "`key_scope`: the key does not allow `admin`; `needs_api_key`: a sandbox token or the operator's key; `destination_limit_reached`: the account has as many destinations, in all its projects, as its plan lets it have; `account_held`: the account is held for its payments, and every call of its keys but `GET /v1/account` is refused so, before the scopes are looked at (`x-error-codes`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "key_scope",
              "needs_api_key",
              "destination_limit_reached",
              "account_held"
            ]
          },
          "404": {
            "description": "`app_not_found`: `apps` names an app the project does not have (`param` `apps`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "app_not_found"
            ]
          },
          "409": {
            "description": "`destination_name_taken`: the project has a destination of that name; `idempotency_key_reused`; `idempotency_in_progress`: the first request with the same key is still being answered after a minute.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "destination_name_taken",
              "idempotency_key_reused",
              "idempotency_in_progress"
            ]
          }
        }
      },
      "get": {
        "operationId": "listDestinations",
        "tags": [
          "Destinations"
        ],
        "summary": "List the project's destinations",
        "description": "In the order of their names, each with how it stands. Takes `read`.",
        "security": [
          {
            "apiKey": [
              "read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of destinations.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DestinationList"
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`, `invalid_field` (`limit`), `invalid_cursor`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter",
              "invalid_field",
              "invalid_cursor"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          }
        }
      }
    },
    "/v1/destinations/{destination}": {
      "parameters": [
        {
          "name": "destination",
          "in": "path",
          "required": true,
          "description": "The destination's name, or its id (`dst-...`).",
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "operationId": "getDestination",
        "tags": [
          "Destinations"
        ],
        "summary": "A destination, and how it stands",
        "description": "With its status (`status`): what the nodes' exporters last said of it, each in the last minute, put together, and what was exported to it today. Takes `read`.",
        "security": [
          {
            "apiKey": [
              "read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "The destination.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Destination"
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`: the call takes none.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          },
          "404": {
            "description": "`destination_not_found`: the project has no destination of that name or id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "destination_not_found"
            ]
          }
        }
      },
      "patch": {
        "operationId": "updateDestination",
        "tags": [
          "Destinations"
        ],
        "summary": "Change a destination",
        "description": "Each field given replaces what it was, but for `headers`, of which each one named is put (null takes it away) and the others are kept. A change of where it is reached or how (its address, a header, `protocol`, `compression`) tries it again: it is connected only if that try is answered 2xx, and the nodes send to it from then on; until then they send it nothing. The nodes are given the change at once, or with their next inventory where they cannot be reached. Takes `admin`.",
        "security": [
          {
            "apiKey": [
              "admin"
            ]
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DestinationUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The destination as it is now, with what a try came to where there was one.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Destination"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_json`, `unknown_field`, `unknown_parameter`; `invalid_name`, `invalid_field`: as when it is made.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter",
              "invalid_name",
              "invalid_field"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          },
          "404": {
            "description": "`destination_not_found`; `app_not_found`: `apps` names an app the project does not have.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "destination_not_found",
              "app_not_found"
            ]
          },
          "409": {
            "description": "`destination_name_taken`: another destination of the project has that name.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "destination_name_taken"
            ]
          }
        }
      },
      "delete": {
        "operationId": "deleteDestination",
        "tags": [
          "Destinations"
        ],
        "summary": "Delete a destination",
        "description": "Nothing is sent to it from the moment each node is told, at once or with its next inventory. What was exported to it is still in the usage of the apps' logs, under its name. Takes `admin`.",
        "security": [
          {
            "apiKey": [
              "admin"
            ]
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted."
          },
          "400": {
            "description": "`unknown_parameter`: the call takes none.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          },
          "404": {
            "description": "`destination_not_found`: the project has no destination of that name or id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "destination_not_found"
            ]
          }
        }
      }
    },
    "/v1/destinations/{destination}/test": {
      "parameters": [
        {
          "name": "destination",
          "in": "path",
          "required": true,
          "description": "The destination's name, or its id (`dst-...`).",
          "schema": {
            "type": "string"
          }
        }
      ],
      "post": {
        "operationId": "testDestination",
        "tags": [
          "Destinations"
        ],
        "summary": "Send a destination one record now",
        "description": "From the control plane, as when it was made: a line of the platform's (`sbx.destination.test`), with the destination's headers, encoding and compression, to the address that was checked. What the endpoint answered is in `test`. A destination that was not connected is connected by an answer of 2xx that refuses no record, and the nodes send to it from then on; one that was is not let go of by an answer that is not. Takes `admin`.",
        "security": [
          {
            "apiKey": [
              "admin"
            ]
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "description": "Nothing: the call takes no field."
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "What the try came to, and the destination as it is now.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DestinationTest"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_json`, `unknown_field`, `unknown_parameter`: the call takes nothing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          },
          "404": {
            "description": "`destination_not_found`: the project has no destination of that name or id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "destination_not_found"
            ]
          }
        }
      }
    },
    "/v1/apps/{app}/instances": {
      "parameters": [
        {
          "$ref": "#/components/parameters/App"
        }
      ],
      "post": {
        "operationId": "createInstance",
        "tags": [
          "Apps"
        ],
        "summary": "The instance of a key of an app: made where the key names none",
        "description": "Idempotent by key. Where the key names no instance, one is made from the template of the version that has the app's traffic, by the path every sandbox is made by (`POST /v1/sandboxes` with `template`: what holds a create there holds here), with the app's labels and `app`, `version`, `region` and `key` (201). Where it names one, that one is woken and answered with (200), and `region` is what it was made in. Two calls at once for a key that names none make one instance, and one of them is answered 201. Takes `write`.",
        "security": [
          {
            "apiKey": [
              "write"
            ]
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/InstanceCreate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The instance the key named, woken.",
            "headers": {
              "Location": {
                "schema": {
                  "type": "string"
                },
                "description": "`/v1/apps/{app id}/instances/{key}`."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Instance"
                }
              }
            }
          },
          "201": {
            "description": "The instance, made now.",
            "headers": {
              "Location": {
                "schema": {
                  "type": "string"
                },
                "description": "`/v1/apps/{app id}/instances/{key}`."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Instance"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_json`, `unknown_field`, `unknown_parameter`; `missing_field`: no `key`; `invalid_key`: a key that is not 1 to 128 characters, or has a control character in it (`param` is `key`); `unknown_region`: a region the app does not run in; `invalid_field`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter",
              "missing_field",
              "invalid_key",
              "unknown_region",
              "invalid_field"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "description": "`key_scope`, `needs_api_key`; what holds the project back (`spend_limit_reached`, and the like), and the plan's largest sandbox, as for a sandbox's create; `account_held`: the account is held for its payments, and every call of its keys but `GET /v1/account` is refused so, before the scopes are looked at (`x-error-codes`). `drive_limit_reached`: a Drive of the key's own (`key_drives`) larger than the plan lets one be, or more than the project's Drives may hold together.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "key_scope",
              "needs_api_key",
              "account_held",
              "drive_limit_reached"
            ]
          },
          "404": {
            "description": "`app_not_found`; `template_not_found`: no machine with room has the version's template.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "app_not_found",
              "template_not_found"
            ]
          },
          "409": {
            "description": "`key_changed` (`retryable`): the key's instance was made and deleted by other calls while this one was worked out, again and again. `managed_by_database`: the app is a database's (`managed_by`), changed through `/v1/databases` alone. `managed_by_sandboxes`: the app is the Sandboxes product's (`managed_by`), whose instances are the project's sandboxes, made, changed and deleted through `/v1/sandboxes`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "key_changed",
              "managed_by_database",
              "managed_by_installation",
              "managed_by_sandboxes"
            ]
          },
          "503": {
            "description": "`app_not_deployed`: the app has no version that is ready; `no_capacity`, `node_unavailable`, `unavailable` (all `retryable`). `not_served_here`: no node of the key's region keeps Drives, and its version gives each key one (`key_drives`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "app_not_deployed",
              "no_capacity",
              "node_unavailable",
              "unavailable",
              "not_served_here"
            ]
          }
        }
      },
      "get": {
        "operationId": "listInstances",
        "tags": [
          "Apps"
        ],
        "summary": "An app's keyed instances",
        "description": "The newest first, each as the control plane last saw it: its object, with the status its node's last inventory gave (`GET …/instances/{key}` asks the node). A key whose instance is being made is listed when it is made. Of an app of the Sandboxes product (`managed_by.object` `sandboxes`: the project's default sandbox app, or an image's), its instances are the project's sandboxes made as its (`POST /v1/sandboxes`, on a node that keeps the app with them), in the order of their keys (a sandbox's name, or its id where it has none; `key` for one), each as the control plane last saw it, with the version of the image it was made of (`version_id`, as the create's answer and `GET /v1/sandboxes/{id}` say it). Takes `read`.",
        "security": [
          {
            "apiKey": [
              "read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "name": "key",
            "in": "query",
            "description": "The instance of this key only.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of instances.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InstanceList"
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`, `invalid_field`, `invalid_cursor`, `invalid_key`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter",
              "invalid_field",
              "invalid_cursor",
              "invalid_key"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          },
          "404": {
            "$ref": "#/components/responses/NoSuchApp"
          }
        }
      }
    },
    "/v1/apps/{app}/instances/{key}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/App"
        },
        {
          "$ref": "#/components/parameters/Key"
        }
      ],
      "get": {
        "operationId": "getInstance",
        "tags": [
          "Apps"
        ],
        "summary": "The instance a key of an app names",
        "description": "As its node shows it now; as it was last seen, `unavailable`, while its node cannot be reached. Of an app of the Sandboxes product (`managed_by.object` `sandboxes`), the project's sandbox the key names (its name, or its id) where it was made as the app's, with the version of the image it was made of. Takes `read`.",
        "security": [
          {
            "apiKey": [
              "read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "The instance.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Instance"
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`, `invalid_key`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter",
              "invalid_key"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          },
          "404": {
            "description": "`app_not_found`; `key_not_found`: the key names no instance (`param` is `key`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "app_not_found",
              "key_not_found"
            ]
          }
        }
      },
      "delete": {
        "operationId": "deleteInstance",
        "tags": [
          "Apps"
        ],
        "summary": "Delete the instance a key of an app names, and free the key",
        "description": "The edges are told first, and the ones that are connected are waited for (a second at the most), so that no request goes to the instance under the key once it is being deleted; then the instance is deleted on its node, with what it had, and booked as any sandbox that is deleted; then the key is free, and the next request that carries it, or call that names it, makes a new instance. Where the instance's node cannot be reached, nothing is deleted and the key names it as before (503). Takes `write`.",
        "security": [
          {
            "apiKey": [
              "write"
            ]
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted, and the key is free."
          },
          "400": {
            "description": "`unknown_parameter`, `invalid_key`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter",
              "invalid_key"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          },
          "404": {
            "description": "`app_not_found`; `key_not_found`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "app_not_found",
              "key_not_found"
            ]
          },
          "409": {
            "description": "`managed_by_database`: the app is a database's (`managed_by`), changed through `/v1/databases` alone. `managed_by_sandboxes`: the app is the Sandboxes product's (`managed_by`), whose instances are the project's sandboxes, made, changed and deleted through `/v1/sandboxes`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "managed_by_database",
              "managed_by_installation",
              "managed_by_sandboxes"
            ]
          },
          "503": {
            "description": "`node_unavailable` (`retryable`): the instance's node cannot be reached, and nothing was deleted. Or the node of one of the key's own Drives (`key_drives`), which go with it: the key is left without an instance, and the same call again deletes the rest.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "node_unavailable"
            ]
          }
        }
      }
    },
    "/v1/apps/{app}/instances/{key}/upgrade": {
      "parameters": [
        {
          "$ref": "#/components/parameters/App"
        },
        {
          "$ref": "#/components/parameters/Key"
        }
      ],
      "post": {
        "operationId": "upgradeInstance",
        "tags": [
          "Apps"
        ],
        "summary": "Move a key's instance to another version of its app, in place",
        "description": "reports/workers/keyed-upgrade.md, slices U1 and U2. A key's instance stays on the version it was made from until this is asked. `version`: the version it goes to, by its id or its number (the app's current version where it is not given), any of the app's versions that is `ready`, an older one too. `when` (slice U3): `now` (the default), or `asleep`, which waits (`waiting`) for the instance to sleep, by its node's last inventory, and begins then, its node making sure it sleeps still (one that woke meanwhile is waited for again): it holds none of the requests the instance serves while it waits; one that never sleeps waits until it is cancelled (`DELETE …/upgrades/{id}`). `keep_days` (0 to 365, 7 by default): how long what a revert needs is kept once it is done, billed as snapshots are (`rollback`): a snapshot of the instance as it was, and one of each Drive it had. The instance keeps its id, its node, its key, its project address, its tokens and its own Drives (`key_drives`) with what they hold; its disk, its machine, its processes, its start command and its labels become the new version's. Its own Drives are matched by their names (reports/workers/keyed-upgrade.md, 五): one the new version names is mounted at the new version's path; one it names that the key has none of is made for the key first, on the instance's node, of the version's `size_bytes`, given what the image has at its path unless the version says `nocopy`, as the key's first instance's are (`drive.created`); one it no longer names is left off, and kept: still the key's, listed, billed as a Drive, deleted with the key, mounted again by a later version that names it. A Drive that cannot be made ends the upgrade `failed`, `upgrade_not_here`, the make's code and words in its `message`, nothing of the instance touched; an upgrade that does not end `done` deletes the Drives it made again, and the key has its Drives as before (one that failed on the instance's node, its version not coming up, reads `failed` a moment before they are gone: their `drive.deleted` events say when). The steps: `preparing`, the instance's node is given the version's template as a deploy gives a node of the app one (had, fetched from the object store, or built), the instance serving meanwhile; `switching`, every edge holds the instance's requests as for a move, the requests its node has in hand are answered first, the calls of its sandbox are answered 503 `upgrading` (but a read of it (`GET /v1/sandboxes/{id}`) answers 200 with it as it was last seen, `status` `upgrading`), and its node stops its start command (the old version's `stop`), takes its Drives off with a version of each made as the old version left it, rolls it over to the new template in place, mounts its Drives again where the new version has them (and the ones made for it; not the ones it leaves off), and starts the new start command; then the key names the new version (`instance.upgraded`), and the held requests go to it, at the pace a move lets them go. A new version that does not come up has the instance put back as it was, its Drives with what they held before (`failed`, `upgrade_failed`, `put_back`; `instance.upgrade_failed`). Open connections end: their processes are replaced. Takes `write`; `Idempotency-Key` is honoured. Of an app of the Sandboxes product (`managed_by.object` `sandboxes`: the project's default sandbox app, or an image's), the instance is the project's sandbox the key names (its name, or its id), and any version of the app is one it may go to (an image the project named for its default sandbox app, or one its tag was moved to): its node is given its own template of the version's image, by its digest, in the sandbox's shape (or the base template), and the sandbox keeps its labels and its settings; its disk and its processes become the new version's, as a restore to a snapshot replaces them; and `version_id`, `image_digest` say what it is from then on.",
        "security": [
          {
            "apiKey": [
              "write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "properties": {
                  "version": {
                    "type": [
                      "string",
                      "integer",
                      "null"
                    ],
                    "description": "The version it goes to: its id (`ver-…`) or its number. Not given: the app's current version."
                  },
                  "when": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "enum": [
                      "now",
                      "asleep",
                      null
                    ],
                    "description": "`now` (the default), or `asleep`: once the instance sleeps."
                  },
                  "keep_days": {
                    "type": [
                      "integer",
                      "null"
                    ],
                    "minimum": 0,
                    "maximum": 365,
                    "description": "Days what a revert needs is kept: 7 where it is not given; 0 for nothing."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The instance is on that version already: the instance, as `GET …/instances/{key}` shows it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Instance"
                }
              }
            }
          },
          "202": {
            "description": "The upgrade, `preparing` (`waiting` with `when: asleep`); `Location` is where it is shown (`?wait_seconds=` waits for its end).",
            "headers": {
              "Location": {
                "description": "`/v1/apps/{app}/instances/{key}/upgrades/{id}`.",
                "schema": {
                  "type": "string"
                }
              },
              "Idempotent-Replayed": {
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                },
                "description": "Present when the answer is the one of an earlier request with the same `Idempotency-Key`."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Upgrade"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_json`, `unknown_field`, `unknown_parameter`, `invalid_field` (`version` is neither an id nor a number; `when`, `keep_days`), `invalid_key`, `invalid_idempotency_key`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter",
              "invalid_field",
              "invalid_key",
              "invalid_idempotency_key"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          },
          "404": {
            "description": "`app_not_found`; `key_not_found`: the key names no instance (`param` is `key`); `version_not_found`: the app has no such version (`param` is `version`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "app_not_found",
              "key_not_found",
              "version_not_found"
            ]
          },
          "409": {
            "description": "`version_not_ready`: the version is being built, or failed, or the app has none deployed (`param` is `version`); `upgrade_in_progress`: the key has an upgrade that is not over (its id is `upgrade_id`, beside `error`); `managed_by_database`: the app is a database's, changed through `/v1/databases` alone; `upgrade_not_here`: of an app of the Sandboxes product, a sandbox made with what its create said of its start (its start command, environment, ready check, runtime or files, or credentials for its image), which the control plane does not keep, or made from a snapshot: its new version could not be made so; `idempotency_key_reused`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "version_not_ready",
              "upgrade_in_progress",
              "managed_by_database",
              "managed_by_installation",
              "upgrade_not_here",
              "idempotency_key_reused"
            ]
          },
          "503": {
            "description": "`moving` (`retryable`, `Retry-After`): the instance is being moved to another node; it is upgraded once that is over.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "moving"
            ]
          }
        }
      }
    },
    "/v1/apps/{app}/instances/{key}/upgrades": {
      "parameters": [
        {
          "$ref": "#/components/parameters/App"
        },
        {
          "$ref": "#/components/parameters/Key"
        }
      ],
      "get": {
        "operationId": "listInstanceUpgrades",
        "tags": [
          "Apps"
        ],
        "summary": "The upgrades of a key's instance",
        "description": "The newest first, the last 100 at the most, on one page. Takes `read`.",
        "security": [
          {
            "apiKey": [
              "read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "The upgrades.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UpgradeList"
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`, `invalid_key`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter",
              "invalid_key"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          },
          "404": {
            "description": "`app_not_found`; `key_not_found`: the key names no instance (`param` is `key`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "app_not_found",
              "key_not_found"
            ]
          }
        }
      }
    },
    "/v1/apps/{app}/instances/{key}/upgrades/{upgrade}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/App"
        },
        {
          "$ref": "#/components/parameters/Key"
        },
        {
          "name": "upgrade",
          "in": "path",
          "required": true,
          "description": "The upgrade's id (`upg-…`).",
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "operationId": "getInstanceUpgrade",
        "tags": [
          "Apps"
        ],
        "summary": "One upgrade of a key's instance",
        "description": "As it is now; with `wait_seconds` (0 to 600), once it is over (`done` or `failed`), or as it is when that time is up. Takes `read`.",
        "security": [
          {
            "apiKey": [
              "read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "wait_seconds",
            "in": "query",
            "description": "Seconds to wait for it to be over, 0 to 600.",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "maximum": 600
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The upgrade.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Upgrade"
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`, `invalid_key`, `invalid_field` (`wait_seconds`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter",
              "invalid_key",
              "invalid_field"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          },
          "404": {
            "description": "`app_not_found`; `key_not_found`: the key names no instance (`param` is `key`); `upgrade_not_found`: the key has no such upgrade.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "app_not_found",
              "key_not_found",
              "upgrade_not_found"
            ]
          }
        }
      },
      "delete": {
        "operationId": "cancelInstanceUpgrade",
        "tags": [
          "Apps"
        ],
        "summary": "Cancel an upgrade that waits",
        "description": "Slice U3. An upgrade that waits (`waiting`: for the instance to sleep, or for its turn in its batch) is cancelled, and is shown so; one cancelled before is shown as it is. One that has begun is not (409 `upgrade_started`). Takes `write`.",
        "security": [
          {
            "apiKey": [
              "write"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "The upgrade, `cancelled`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Upgrade"
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`, `invalid_key`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter",
              "invalid_key"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          },
          "404": {
            "description": "`app_not_found`; `key_not_found`: the key names no instance (`param` is `key`); `upgrade_not_found`: the key has no such upgrade.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "app_not_found",
              "key_not_found",
              "upgrade_not_found"
            ]
          },
          "409": {
            "description": "`upgrade_started`: it has begun, or is over (its id is `upgrade_id`, beside `error`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "upgrade_started"
            ]
          }
        }
      }
    },
    "/v1/apps/{app}/instances/{key}/upgrades/{upgrade}/revert": {
      "parameters": [
        {
          "$ref": "#/components/parameters/App"
        },
        {
          "$ref": "#/components/parameters/Key"
        },
        {
          "name": "upgrade",
          "in": "path",
          "required": true,
          "description": "The upgrade to go back from (`upg-…`).",
          "schema": {
            "type": "string"
          }
        }
      ],
      "post": {
        "operationId": "revertInstanceUpgrade",
        "tags": [
          "Apps"
        ],
        "summary": "Put a key's instance back to what an upgrade of it kept",
        "description": "Slice U3 of reports/workers/keyed-upgrade.md. The instance goes back to the moment before the upgrade: its disk the snapshot it kept, each of its Drives what it held then (made that version again, after a snapshot of what it holds now, `before-revert-<id>`), its version the one the upgrade came from; an instance without Drives has its processes back as they were then, one with Drives boots from that disk and starts its version's start command once its Drives are mounted. A new upgrade of `kind: revert` (`reverts` names the upgrade), which goes as an upgrade does (`preparing`, `switching`, its requests held at the edges) and keeps what a revert of it needs in the same way (`keep_days`), so a revert can be reverted; `instance.reverted` once it is done. What it goes back to has to be kept still: refused 409 `rollback_gone` where the upgrade kept nothing, its days are over, or its snapshots were deleted. Takes `write`; `Idempotency-Key` is honoured: the same call again is the revert it made, not another over what was written since.",
        "security": [
          {
            "apiKey": [
              "write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "properties": {
                  "keep_days": {
                    "type": [
                      "integer",
                      "null"
                    ],
                    "minimum": 0,
                    "maximum": 365,
                    "description": "Days what a revert of this revert needs is kept: 7 where it is not given; 0 for nothing."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "The revert, `preparing`; `Location` is where it is shown.",
            "headers": {
              "Location": {
                "description": "`/v1/apps/{app}/instances/{key}/upgrades/{id}`.",
                "schema": {
                  "type": "string"
                }
              },
              "Idempotent-Replayed": {
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                },
                "description": "Present when the answer is the one of an earlier request with the same `Idempotency-Key`."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Upgrade"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_json`, `unknown_field`, `unknown_parameter`, `invalid_field` (`keep_days`), `invalid_key`, `invalid_idempotency_key`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter",
              "invalid_field",
              "invalid_key",
              "invalid_idempotency_key"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          },
          "404": {
            "description": "`app_not_found`; `key_not_found`: the key names no instance (`param` is `key`); `upgrade_not_found`: the key has no such upgrade.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "app_not_found",
              "key_not_found",
              "upgrade_not_found"
            ]
          },
          "409": {
            "description": "`rollback_gone`: what it goes back to is not kept any more (the message says which part); `upgrade_in_progress`: the key has an upgrade that is not over, this one perhaps (its id is `upgrade_id`, beside `error`); `managed_by_database`; `idempotency_key_reused`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "rollback_gone",
              "upgrade_in_progress",
              "managed_by_database",
              "managed_by_installation",
              "idempotency_key_reused"
            ]
          },
          "503": {
            "description": "`moving` (`retryable`, `Retry-After`): the instance is being moved to another node.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "moving"
            ]
          }
        }
      }
    },
    "/v1/apps/{app}/upgrades": {
      "parameters": [
        {
          "$ref": "#/components/parameters/App"
        }
      ],
      "post": {
        "operationId": "createUpgradeBatch",
        "tags": [
          "Apps"
        ],
        "summary": "Upgrade an app's keys' instances, a batch at a time",
        "description": "Slice U4 of reports/workers/keyed-upgrade.md. Every key of the app whose instance is not on `version` (the app's current version where it is not given), of `from_versions` where it says, among `keys` where it says, gets an upgrade that names the batch, `waiting`. They are taken up as they may be: `when` `asleep` (the default) once each instance sleeps, `now` at once; at most `concurrency` of the batch under way at once (4 by default), at most two on one node. When `max_failures` of them have failed (5 by default) the batch stops (`failed`): those that waited are cancelled, those under way go on, and those done stay on the new version. A key that has an upgrade of its own that is not over is left out (`skipped`). An instance that never sleeps keeps its member `waiting` until the batch is stopped. `keep_days` as for one upgrade. An app that says `keys.upgrade_on_deploy` gets such a batch, with the defaults, from every deploy, which stops the one an earlier deploy made. Takes `write`; `Idempotency-Key` is honoured.",
        "security": [
          {
            "apiKey": [
              "write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "properties": {
                  "version": {
                    "type": [
                      "string",
                      "integer",
                      "null"
                    ],
                    "description": "The version they go to: its id or its number; the app's current one where it is not given."
                  },
                  "from_versions": {
                    "type": [
                      "array",
                      "null"
                    ],
                    "items": {
                      "type": [
                        "string",
                        "integer"
                      ]
                    },
                    "description": "Only the keys on these versions."
                  },
                  "keys": {
                    "type": [
                      "array",
                      "null"
                    ],
                    "items": {
                      "type": "string"
                    },
                    "description": "Only these keys."
                  },
                  "when": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "enum": [
                      "now",
                      "asleep",
                      null
                    ],
                    "description": "`asleep` (the default): each once its instance sleeps; `now`: each as its turn comes."
                  },
                  "keep_days": {
                    "type": [
                      "integer",
                      "null"
                    ],
                    "minimum": 0,
                    "maximum": 365,
                    "description": "Days what a revert of each needs is kept: 7 where it is not given."
                  },
                  "concurrency": {
                    "type": [
                      "integer",
                      "null"
                    ],
                    "minimum": 1,
                    "maximum": 64,
                    "description": "Members under way at once, at the most: 4 where it is not given."
                  },
                  "max_failures": {
                    "type": [
                      "integer",
                      "null"
                    ],
                    "minimum": 1,
                    "maximum": 100000,
                    "description": "Failures that stop the batch: 5 where it is not given."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "The batch; `Location` is where it is shown (`?wait_seconds=` waits for its end).",
            "headers": {
              "Location": {
                "description": "`/v1/apps/{app}/upgrades/{id}`.",
                "schema": {
                  "type": "string"
                }
              },
              "Idempotent-Replayed": {
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                },
                "description": "Present when the answer is the one of an earlier request with the same `Idempotency-Key`."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UpgradeBatch"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_json`, `unknown_field`, `unknown_parameter`, `invalid_field` (`version`, `from_versions`, `keys`, `when`, `keep_days`, `concurrency`, `max_failures`), `invalid_idempotency_key`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter",
              "invalid_field",
              "invalid_idempotency_key"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          },
          "404": {
            "description": "`app_not_found`; `version_not_found` (`param` is `version` or `from_versions`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "app_not_found",
              "version_not_found"
            ]
          },
          "409": {
            "description": "`version_not_ready` (`param` is `version`); `managed_by_database`; `idempotency_key_reused`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "version_not_ready",
              "managed_by_database",
              "managed_by_installation",
              "idempotency_key_reused"
            ]
          }
        }
      },
      "get": {
        "operationId": "listUpgradeBatches",
        "tags": [
          "Apps"
        ],
        "summary": "An app's batches of upgrades",
        "description": "The newest first, the last 100 at the most, on one page. Takes `read`.",
        "security": [
          {
            "apiKey": [
              "read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "The batches.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UpgradeBatchList"
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          },
          "404": {
            "description": "`app_not_found`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "app_not_found"
            ]
          }
        }
      }
    },
    "/v1/apps/{app}/upgrades/{batch}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/App"
        },
        {
          "name": "batch",
          "in": "path",
          "required": true,
          "description": "The batch's id (`upb-…`).",
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "operationId": "getUpgradeBatch",
        "tags": [
          "Apps"
        ],
        "summary": "One batch of upgrades",
        "description": "As it is now; with `wait_seconds` (0 to 600), once it is over (no longer `running`), or as it is when that time is up. Takes `read`.",
        "security": [
          {
            "apiKey": [
              "read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "wait_seconds",
            "in": "query",
            "description": "Seconds to wait for it to be over, 0 to 600.",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "maximum": 600
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The batch.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UpgradeBatch"
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`, `invalid_field` (`wait_seconds`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter",
              "invalid_field"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          },
          "404": {
            "description": "`app_not_found`; `batch_not_found`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "app_not_found",
              "batch_not_found"
            ]
          }
        }
      }
    },
    "/v1/apps/{app}/upgrades/{batch}/stop": {
      "parameters": [
        {
          "$ref": "#/components/parameters/App"
        },
        {
          "name": "batch",
          "in": "path",
          "required": true,
          "description": "The batch's id (`upb-…`).",
          "schema": {
            "type": "string"
          }
        }
      ],
      "post": {
        "operationId": "stopUpgradeBatch",
        "tags": [
          "Apps"
        ],
        "summary": "Stop a batch of upgrades",
        "description": "Its members that wait are cancelled; those under way go on to their end, and those done stay on the new version. The batch is `stopped` (`stop_reason: asked`); one that is over already is shown as it is. Takes `write`.",
        "security": [
          {
            "apiKey": [
              "write"
            ]
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "properties": {}
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The batch.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UpgradeBatch"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_json`, `unknown_field`, `unknown_parameter`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          },
          "404": {
            "description": "`app_not_found`; `batch_not_found`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "app_not_found",
              "batch_not_found"
            ]
          }
        }
      }
    },
    "/v1/apps/{app}/instances/{key}/tcp": {
      "parameters": [
        {
          "$ref": "#/components/parameters/App"
        },
        {
          "$ref": "#/components/parameters/Key"
        }
      ],
      "get": {
        "operationId": "listInstanceTcpPorts",
        "tags": [
          "Apps"
        ],
        "summary": "The TCP ports open at a key's instance's IPv6 address",
        "description": "The call of the instance's sandbox (`GET /v1/sandboxes/{id}/tcp`), on whichever node it is: the TCP ports open to the internet at its own IPv6 address, each with its sources. Takes `read`.",
        "security": [
          {
            "apiKey": [
              "read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "The TCP ports open.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InstancePortList"
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`: as the call of the instance's sandbox says them (`sandboxd/openapi/openapi.json`); `invalid_key`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter",
              "invalid_key"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          },
          "404": {
            "description": "`app_not_found`; `key_not_found`: the key names no instance (`param` is `key`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "app_not_found",
              "key_not_found"
            ]
          },
          "503": {
            "description": "`node_unavailable` (`retryable`): the instance's node cannot be reached, and nothing was done.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "node_unavailable"
            ]
          }
        }
      }
    },
    "/v1/apps/{app}/instances/{key}/tcp/{port}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/App"
        },
        {
          "$ref": "#/components/parameters/Key"
        },
        {
          "$ref": "#/components/parameters/InstancePortNumber"
        }
      ],
      "put": {
        "operationId": "openInstanceTcpPort",
        "tags": [
          "Apps"
        ],
        "summary": "Open a TCP port at a key's instance's IPv6 address",
        "description": "The call of the instance's sandbox (`PUT /v1/sandboxes/{id}/tcp/{port}`), on whichever node it is: the port is open to the sources `from` lists and to nobody else (`::/0` is everybody, and has to be said); inbound is closed by default. The instance keeps it through sleep and moves; a new instance of the key does not have it. Takes `write`.",
        "security": [
          {
            "apiKey": [
              "write"
            ]
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/InstancePortOpen"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The port as it is open now, and where it is reached.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InstancePort"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_json`, `unknown_field`, `unknown_parameter`, `invalid_port`, `invalid_from`, `invalid_inbound`: as the call of the instance's sandbox says them (`sandboxd/openapi/openapi.json`); `invalid_key`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter",
              "invalid_port",
              "invalid_from",
              "invalid_inbound",
              "invalid_key"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          },
          "404": {
            "description": "`app_not_found`; `key_not_found`: the key names no instance (`param` is `key`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "app_not_found",
              "key_not_found"
            ]
          },
          "409": {
            "description": "`no_ipv6`: the instance has no IPv6 address of its own (its node has no pool of addresses). `managed_by_database`: the app is a database's (`managed_by`), changed through `/v1/databases` alone. `managed_by_sandboxes`: the app is the Sandboxes product's (`managed_by`), whose instances are the project's sandboxes, made, changed and deleted through `/v1/sandboxes`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "no_ipv6",
              "managed_by_database",
              "managed_by_installation",
              "managed_by_sandboxes"
            ]
          },
          "503": {
            "description": "`node_unavailable` (`retryable`): the instance's node cannot be reached, and nothing was done.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "node_unavailable"
            ]
          }
        }
      },
      "delete": {
        "operationId": "closeInstanceTcpPort",
        "tags": [
          "Apps"
        ],
        "summary": "Close a TCP port at a key's instance's IPv6 address",
        "description": "The call of the instance's sandbox (`DELETE /v1/sandboxes/{id}/tcp/{port}`): new connections to it are refused from here on, those it has go on. Takes `write`.",
        "security": [
          {
            "apiKey": [
              "write"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "Closed; `was_open` says whether it was open.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InstancePortClosed"
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`, `invalid_port`: as the call of the instance's sandbox says them (`sandboxd/openapi/openapi.json`); `invalid_key`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter",
              "invalid_port",
              "invalid_key"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          },
          "404": {
            "description": "`app_not_found`; `key_not_found`: the key names no instance (`param` is `key`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "app_not_found",
              "key_not_found"
            ]
          },
          "409": {
            "description": "`managed_by_database`: the app is a database's (`managed_by`), changed through `/v1/databases` alone. `managed_by_sandboxes`: the app is the Sandboxes product's (`managed_by`), whose instances are the project's sandboxes, made, changed and deleted through `/v1/sandboxes`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "managed_by_database",
              "managed_by_installation",
              "managed_by_sandboxes"
            ]
          },
          "503": {
            "description": "`node_unavailable` (`retryable`): the instance's node cannot be reached, and nothing was done.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "node_unavailable"
            ]
          }
        }
      }
    },
    "/v1/apps/{app}/instances/{key}/udp": {
      "parameters": [
        {
          "$ref": "#/components/parameters/App"
        },
        {
          "$ref": "#/components/parameters/Key"
        }
      ],
      "get": {
        "operationId": "listInstanceUdpPorts",
        "tags": [
          "Apps"
        ],
        "summary": "The UDP ports open at a key's instance's IPv6 address",
        "description": "The call of the instance's sandbox (`GET /v1/sandboxes/{id}/udp`), on whichever node it is: the UDP ports open to the internet at its own IPv6 address, each with its sources. Takes `read`.",
        "security": [
          {
            "apiKey": [
              "read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "The UDP ports open.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InstancePortList"
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`: as the call of the instance's sandbox says them (`sandboxd/openapi/openapi.json`); `invalid_key`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter",
              "invalid_key"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          },
          "404": {
            "description": "`app_not_found`; `key_not_found`: the key names no instance (`param` is `key`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "app_not_found",
              "key_not_found"
            ]
          },
          "503": {
            "description": "`node_unavailable` (`retryable`): the instance's node cannot be reached, and nothing was done.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "node_unavailable"
            ]
          }
        }
      }
    },
    "/v1/apps/{app}/instances/{key}/udp/{port}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/App"
        },
        {
          "$ref": "#/components/parameters/Key"
        },
        {
          "$ref": "#/components/parameters/InstancePortNumber"
        }
      ],
      "put": {
        "operationId": "openInstanceUdpPort",
        "tags": [
          "Apps"
        ],
        "summary": "Open a UDP port at a key's instance's IPv6 address",
        "description": "The call of the instance's sandbox (`PUT /v1/sandboxes/{id}/udp/{port}`), on whichever node it is: the port is open to the sources `from` lists and to nobody else (`::/0` is everybody, and has to be said); inbound is closed by default. The instance keeps it through sleep and moves; a new instance of the key does not have it. Takes `write`.",
        "security": [
          {
            "apiKey": [
              "write"
            ]
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/InstancePortOpen"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The port as it is open now, and where it is reached.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InstancePort"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_json`, `unknown_field`, `unknown_parameter`, `invalid_port`, `invalid_from`, `invalid_inbound`: as the call of the instance's sandbox says them (`sandboxd/openapi/openapi.json`); `invalid_key`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter",
              "invalid_port",
              "invalid_from",
              "invalid_inbound",
              "invalid_key"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          },
          "404": {
            "description": "`app_not_found`; `key_not_found`: the key names no instance (`param` is `key`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "app_not_found",
              "key_not_found"
            ]
          },
          "409": {
            "description": "`no_ipv6`: the instance has no IPv6 address of its own (its node has no pool of addresses). `managed_by_database`: the app is a database's (`managed_by`), changed through `/v1/databases` alone. `managed_by_sandboxes`: the app is the Sandboxes product's (`managed_by`), whose instances are the project's sandboxes, made, changed and deleted through `/v1/sandboxes`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "no_ipv6",
              "managed_by_database",
              "managed_by_installation",
              "managed_by_sandboxes"
            ]
          },
          "503": {
            "description": "`node_unavailable` (`retryable`): the instance's node cannot be reached, and nothing was done.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "node_unavailable"
            ]
          }
        }
      },
      "delete": {
        "operationId": "closeInstanceUdpPort",
        "tags": [
          "Apps"
        ],
        "summary": "Close a UDP port at a key's instance's IPv6 address",
        "description": "The call of the instance's sandbox (`DELETE /v1/sandboxes/{id}/udp/{port}`): new connections to it are refused from here on, those it has go on. Takes `write`.",
        "security": [
          {
            "apiKey": [
              "write"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "Closed; `was_open` says whether it was open.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InstancePortClosed"
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`, `invalid_port`: as the call of the instance's sandbox says them (`sandboxd/openapi/openapi.json`); `invalid_key`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter",
              "invalid_port",
              "invalid_key"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          },
          "404": {
            "description": "`app_not_found`; `key_not_found`: the key names no instance (`param` is `key`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "app_not_found",
              "key_not_found"
            ]
          },
          "409": {
            "description": "`managed_by_database`: the app is a database's (`managed_by`), changed through `/v1/databases` alone. `managed_by_sandboxes`: the app is the Sandboxes product's (`managed_by`), whose instances are the project's sandboxes, made, changed and deleted through `/v1/sandboxes`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "managed_by_database",
              "managed_by_installation",
              "managed_by_sandboxes"
            ]
          },
          "503": {
            "description": "`node_unavailable` (`retryable`): the instance's node cannot be reached, and nothing was done.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "node_unavailable"
            ]
          }
        }
      }
    },
    "/v1/apps/{app}/key-secrets": {
      "parameters": [
        {
          "$ref": "#/components/parameters/App"
        }
      ],
      "post": {
        "operationId": "createKeySecret",
        "tags": [
          "Apps"
        ],
        "summary": "Make a secret an app's keys are signed with",
        "description": "A request through an edge for an app with keys (`keys.from`) goes on only with its key signed by one of the app's secrets: `<key>.<expiry>.<signature>` where it carries the key, the signature HMAC-SHA256 keyed with the secret over the app's id, the key and the expiry (Unix seconds, or nothing for none), one line each, in base64url without padding (`x-edge-router` 403; control/README.md, 带键的实例 「签名的键」; the SDKs and `glac cube keys sign` sign so). The secret is in this answer and in no other: the store keeps it sealed. An app has two at the most, so that its owner can move to a new one: make it, move what signs to it, delete the old one; a signature by either goes on while both are there. The call answers once the edges that are connected have the secret, a second at the most. Takes `write`. An `Idempotency-Key` makes the same call again within a day answer what the first made, the secret with it.",
        "security": [
          {
            "apiKey": [
              "write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "description": "Nothing: an empty body, or `{}`.",
                "properties": {}
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The secret, with its value.",
            "headers": {
              "Location": {
                "schema": {
                  "type": "string"
                },
                "description": "`/v1/apps/{app id}/key-secrets/{id}`."
              },
              "Idempotent-Replayed": {
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                },
                "description": "Present when the answer is the one of an earlier request with the same `Idempotency-Key`."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KeySecretMade"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_json`: a body that is no JSON object; `unknown_field`; `unknown_parameter`; `invalid_idempotency_key`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter",
              "invalid_idempotency_key"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "description": "`key_scope`, `needs_api_key`, `account_held` (`x-error-codes`); `key_secret_limit_reached`: the app has two secrets, and keeps them.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "key_scope",
              "needs_api_key",
              "account_held",
              "key_secret_limit_reached"
            ]
          },
          "404": {
            "description": "`app_not_found`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "app_not_found"
            ]
          },
          "409": {
            "description": "`idempotency_key_reused`: the same Idempotency-Key with another body. `managed_by_database`: the app is a database's (`managed_by`), changed through `/v1/databases` alone. `managed_by_sandboxes`: the app is the Sandboxes product's (`managed_by`), whose instances are the project's sandboxes, made, changed and deleted through `/v1/sandboxes`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "idempotency_key_reused",
              "managed_by_database",
              "managed_by_installation",
              "managed_by_sandboxes"
            ]
          }
        }
      },
      "get": {
        "operationId": "listKeySecrets",
        "tags": [
          "Apps"
        ],
        "summary": "An app's key secrets, without their values",
        "description": "The newest first, each by its first characters (`prefix`). Takes `read`.",
        "security": [
          {
            "apiKey": [
              "read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of secrets.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KeySecretList"
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`, `invalid_field`, `invalid_cursor`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter",
              "invalid_field",
              "invalid_cursor"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          },
          "404": {
            "description": "`app_not_found`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "app_not_found"
            ]
          }
        }
      }
    },
    "/v1/apps/{app}/key-secrets/{secret}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/App"
        },
        {
          "$ref": "#/components/parameters/KeySecret"
        }
      ],
      "get": {
        "operationId": "getKeySecret",
        "tags": [
          "Apps"
        ],
        "summary": "One of an app's key secrets, without its value",
        "description": "Takes `read`.",
        "security": [
          {
            "apiKey": [
              "read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "The secret, without its value.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KeySecret"
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          },
          "404": {
            "description": "`app_not_found`; `key_secret_not_found`: the app has no secret of this id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "app_not_found",
              "key_secret_not_found"
            ]
          }
        }
      },
      "delete": {
        "operationId": "deleteKeySecret",
        "tags": [
          "Apps"
        ],
        "summary": "Delete one of an app's key secrets",
        "description": "From the answer on, no edge that is connected lets through a key it signed: the call answers once they have the table without it, a second at the most. An edge that cannot reach the control plane goes by the table it has until it reaches it again. Takes `write`.",
        "security": [
          {
            "apiKey": [
              "write"
            ]
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted."
          },
          "400": {
            "description": "`unknown_parameter`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          },
          "404": {
            "description": "`app_not_found`; `key_secret_not_found`: the app has no secret of this id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "app_not_found",
              "key_secret_not_found"
            ]
          },
          "409": {
            "description": "`managed_by_database`: the app is a database's (`managed_by`), changed through `/v1/databases` alone. `managed_by_sandboxes`: the app is the Sandboxes product's (`managed_by`), whose instances are the project's sandboxes, made, changed and deleted through `/v1/sandboxes`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "managed_by_database",
              "managed_by_installation",
              "managed_by_sandboxes"
            ]
          }
        }
      }
    },
    "/v1/billing/topups": {
      "post": {
        "operationId": "createTopup",
        "tags": [
          "Billing"
        ],
        "summary": "Ask for a top-up",
        "description": "A top-up of the account of the key's project: an amount in US dollars, in whole cents, $20 at the least (as the control plane is started by default) and $1,000,000 at the most. It is `to_collect` when this answers; collected, it is as many Credits as dollars, in a lot that lapses 365 days after. It moves no account's plan: on any plan it is the balance that pays for what is used past what the plan includes. With `Idempotency-Key`, the same key and body again, from any project of the account and however much later, is answered with the top-up they made, as it is then, and makes no other. Collected where the platform is registered for tax, the tax is added to what the card pays, and the Credits are the amount before tax: an Australian who tops up $20 pays $22 and has 20 Credits (`tax` of the charge, control/README.md, 税). An account that is no business, where only businesses are sold to, is refused (403 `tax_region_business_only`): where it is is its billing address, or, where it has none, the country of the address the call comes from (`--ip-country-file`). The address the call comes from is kept with the top-up, as one of the evidences of where its customer is. Takes `admin`.",
        "security": [
          {
            "apiKey": [
              "admin"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TopupCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The top-up.",
            "headers": {
              "Idempotent-Replayed": {
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                },
                "description": "Present when the top-up is the one an earlier request with the same `Idempotency-Key` made."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Charge"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_json`, `unknown_field`, `unknown_parameter`, `invalid_idempotency_key`; `invalid_field`: `amount_usd` is not a number of dollars in whole cents between the smallest top-up and the largest; `start_now_required`: the account buys as a consumer in the EEA or the UK and did not ask for what it buys to be provided at once (`start_now`), and nothing was made.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter",
              "invalid_field",
              "invalid_idempotency_key",
              "start_now_required"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/TrustLimitOrTaxRegion"
          },
          "409": {
            "description": "`idempotency_key_reused`: the account has used this `Idempotency-Key` with another body; `billing_address_required` (`param` `billing_address`): the platform computes tax with Stripe Tax and Stripe collects, and Stripe Tax has no place for the account: it has no billing address, and the country of its card on record is not known (set its address with `PATCH /v1/account`, or pay on a payment page, `POST /v1/billing/checkout`, which asks for it). Nothing was made.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "idempotency_key_reused",
              "billing_address_required"
            ]
          }
        }
      }
    },
    "/v1/billing/checkout": {
      "post": {
        "operationId": "createCheckout",
        "tags": [
          "Billing"
        ],
        "summary": "A payment page of Stripe's, on which the account pays a charge with a card of its own",
        "description": "Makes a charge of the account of the key's project and a Stripe Checkout page for it: a top-up (`purpose` `topup`, with `amount_usd` as `POST /v1/billing/topups` takes it), or what asking for Paid charges (`purpose` `subscription`, with `plan` `paid`, as `PATCH /v1/billing` asks for it: Paid's fee for its first period). The page's line is a price of the plan's product (one Stripe product, `plan_paid`), and what the account owed is said beside the button that pays. The charge is `to_collect` and is given to no collector: it is answered by what Stripe says of the page. The page is for exactly the charge's amount in US dollars, with the tax added to it where the platform is registered (`--tax stripe`: Stripe Tax computes it for the billing address given on the page, as a line of its own, the price being without tax), asks for the billing address, all of it, and for a business's tax id (both saved to the account's customer, and the account's from then on), takes cards only, asks for 3-D Secure (`request_three_d_secure` `any`), saves the card to the account's customer for what is charged later when nobody is there (renewals, auto reload), and ends an hour after it was made. Paid, the charge is collected (a top-up is Credits from that moment, as many as its dollars before tax; a subscription's period begins then), and the card it was paid with is recorded and is the account's from then on; a card that is already the card of `--cards-per-fingerprint` other accounts (3 by default) is refunded in full at once, and the charge fails with `card_used_elsewhere`. A card declined on the page leaves the page open for another; a page that ends unpaid fails its charge with `checkout_expired`. With `Idempotency-Key`, the same key and body again is answered with the same page, as a replay. A page that is open for the charge of the same plan is the answer to asking for it again (200). An account that owes may call it: it is how it pays. An account that is no business, where only businesses are sold to, is refused before anything is asked of Stripe (403 `tax_region_business_only`): where it is is its billing address, or, where it has none, the country of the address the call comes from; where neither is known the page asks for the address, and a payment made from a place sold only to businesses by an account that is none is held for an operator (control/README.md, 税). Whoever paid on the page is sent back to the account's billing page in the console (`<--console-url>#/a/<account id>/billing?back=paid&charge=<the charge's id>`), which follows the charge until it is answered, and whoever went back from it to the same page with `?back=cancelled`; a page the operator names (`--checkout-success-url`, `--checkout-cancel-url`) is the one its flag says. Takes `admin`. Needs a control plane started with `--stripe-key-file`, `--stripe-webhook-secret-file`, and `--console-url` or `--checkout-success-url`.",
        "security": [
          {
            "apiKey": [
              "admin"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CheckoutCreate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The page that is open for the charge of the same plan, asked for again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Checkout"
                }
              }
            }
          },
          "201": {
            "description": "The page, made now.",
            "headers": {
              "Idempotent-Replayed": {
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                },
                "description": "Present when the page is the one an earlier request with the same `Idempotency-Key` made."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Checkout"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_json`, `unknown_field`, `unknown_parameter`, `invalid_idempotency_key`; `invalid_field`: `purpose` is not `topup` or `subscription`, `amount_usd` is not a number of dollars in whole cents between the smallest top-up and the largest, `plan` is not a paid plan, or a top-up names a plan or a subscription an amount; `start_now_required`: the account buys as a consumer in the EEA or the UK and did not ask for what it buys to be provided at once (`start_now`), and nothing was made.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter",
              "invalid_field",
              "invalid_idempotency_key",
              "start_now_required"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/TrustLimitTaxRegionOrPlan"
          },
          "409": {
            "description": "`idempotency_key_reused`: the account has used this `Idempotency-Key` with another body; `subscription_charge_pending`: a charge of the account's subscription of another plan has no answer (its page is open, or it is being collected from the account's card); `nothing_to_pay`: what was asked for is had without a payment, or is so already (the account is on the plan; its subscription was to end and `PATCH /v1/billing` has it go on; a plan that stands lower, from the next period), and nothing was changed; `checkout_ended`: the call with this `Idempotency-Key` made a charge whose page Stripe never made, and the charge has failed; `stripe_not_configured`: the control plane makes no pages.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "idempotency_key_reused",
              "subscription_charge_pending",
              "nothing_to_pay",
              "checkout_ended",
              "stripe_not_configured"
            ]
          },
          "502": {
            "description": "`stripe_refused`: Stripe answered, and made no page; the charge failed, and nothing was paid. The message has Stripe's words and the id of its request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "stripe_refused"
            ]
          },
          "503": {
            "description": "`stripe_unavailable`: Stripe could not be reached. With `Idempotency-Key` the charge waits for the same call again, which makes the page Stripe made the first time if it did; without one it has failed. `retryable`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "stripe_unavailable"
            ]
          }
        }
      }
    },
    "/v1/billing/withdrawals": {
      "get": {
        "operationId": "listWithdrawals",
        "tags": [
          "Billing"
        ],
        "summary": "What the account may withdraw from, and the withdrawals it made",
        "description": "The withdrawal function (the Consumer Rights Directive 2011/83/EU, Article 11a; the terms, 21.4): a consumer in the European Economic Area or the United Kingdom may withdraw from a purchase within 14 days, without giving any reason. `withdrawable` is what the account may withdraw from now, the newest first: each purchase (a top-up, an auto reload, the charge of the first period of a plan or of a move up; not a renewal, and not what paid what was owed on the billing day) whose buyer was no business (no tax id that was verified or being verified when it paid) in the EEA or the UK, as the evidence of its payment says (control/README.md, 税), collected less than fifteen days ago (the fourteen days of the right begin the day after the purchase, in the buyer's time: fifteen days from the moment cover the end of the fourteenth in every time zone of those places), with what withdrawing from it now would give back. A top-up whose buyer asked for it at once and whose Credits are all used is not there: it has been provided in full; nor is a plan's charge whose period is over, or was cut short by a move up. `right` says where the account buys from now and whether a purchase it makes now gives it the right, and the words of the request to be provided at once that such a purchase carries (`start_now`). `data` are the withdrawals the account made, the newest first, a page at a time. An account that is held for its payments may call it. Takes `read`.",
        "security": [
          {
            "apiKey": [
              "read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "What may be withdrawn from, and a page of the withdrawals made.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WithdrawalList"
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`; `invalid_field`: `limit` is not a number from 1 to 1000; `invalid_cursor`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter",
              "invalid_field",
              "invalid_cursor"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "description": "`key_scope`: the key does not allow `read`; `needs_api_key`: the operator's key, or the SSH gateway's.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "key_scope",
              "needs_api_key"
            ]
          }
        }
      },
      "post": {
        "operationId": "createWithdrawal",
        "tags": [
          "Billing"
        ],
        "summary": "Withdraw from a purchase: the statement of withdrawal",
        "description": "The statement that the account's buyer withdraws from a purchase (`charge_id`, one that `GET /v1/billing/withdrawals` lists), with its name, and the address the acknowledgement of receipt goes to (`email`; the account's own where it is left out): what the console's billing page sends with its button `Confirm withdrawal`. Received, at once and in one transaction: the charge is `refunded` (`refund_reason` `withdrawal`); of a top-up, the Credits left of it are taken from the balance (an entry `refund` of the ledger), and the account's auto reload, where it is on, is turned off (`auto_reload_off`); of a plan's charge, its period and the subscription end there, and the account is on Free from that moment (`subscription.ended`, `reason` `withdrawn`); the acknowledgement of receipt is sent by mail at once, with what was received, its date and time, and what happens next (the notice `withdrawal_received`); every project of the account is told (`withdrawal.received`). What is given back (`rule`): of a purchase whose buyer did not ask for it at once, all that was paid (`in_full`); of a top-up that was asked for at once, what is left of its Credits, rounded up to the cent (`credits_used`); of a plan's charge that was asked for at once, what was paid less the share of it that the larger of two comes to, the part of the period gone by and the part used of what it includes of the meter most used, rounded down to the cent (`share`). What a plan's charge carried of a debt is not given back. The money goes back to the card it was paid with, with the tax that was charged on it: at Stripe by a credit note of the charge's invoice (`withdrawal.refunded`, and the credit note in `GET /v1/billing/invoices`), tried again every minute while Stripe cannot be reached; with what stands in for a collector, in the books at once. With `Idempotency-Key`, the same key and body again is answered with what it made. An account that is held for its payments may call it. Takes `admin`; with the console's session, the account's owners'.",
        "security": [
          {
            "apiKey": [
              "admin"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WithdrawalCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The withdrawal, received.",
            "headers": {
              "Idempotent-Replayed": {
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                },
                "description": "Present when the withdrawal is the one an earlier request with the same `Idempotency-Key` made."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Withdrawal"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_json`, `unknown_field`, `unknown_parameter`, `invalid_idempotency_key`; `missing_field`: no `charge_id`, no `name`, or no `email` of an account that has no address its notices go to; `invalid_field`: `name` is not 1 to 200 characters, `email` is no address.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter",
              "invalid_idempotency_key",
              "missing_field",
              "invalid_field"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "description": "`key_scope`: the key does not allow `admin`; `needs_api_key`: the operator's key, or the SSH gateway's; `not_a_consumer`: the purchase was made by a business (the account had a tax id when it paid), or from outside the EEA and the UK, or from nowhere that is known; the message says which.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "key_scope",
              "needs_api_key",
              "not_a_consumer"
            ]
          },
          "404": {
            "description": "`charge_not_found`: the account has no such charge.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "charge_not_found"
            ]
          },
          "409": {
            "description": "`withdrawal_period_over`: the fourteen days are over (the message says when it was bought, and when they ended); `nothing_to_withdraw`: it is no purchase to withdraw from (it is not collected, failed or was refunded; it paid what was owed; it renewed a subscription; the period it paid for is over; a top-up asked for at once whose Credits are all used, which has been provided in full), and the message says which; `already_withdrawn`: it was withdrawn from before (the message names the withdrawal); `subscription_charge_pending`: a charge of the account's subscription is being collected, `retryable`; `idempotency_key_reused`: the account has used this `Idempotency-Key` with another body.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "withdrawal_period_over",
              "nothing_to_withdraw",
              "already_withdrawn",
              "subscription_charge_pending",
              "idempotency_key_reused"
            ]
          }
        }
      }
    },
    "/v1/billing/portal": {
      "post": {
        "operationId": "openBillingPortal",
        "tags": [
          "Billing"
        ],
        "summary": "Stripe's customer portal, in which the account changes its card, reads its invoices and changes its billing address and tax id",
        "description": "A session of Stripe's customer portal for the account's customer at Stripe: its address, which the account opens in a browser and which lasts a short while (a call asks for another each time). The portal is of a configuration the control plane makes at Stripe once a mode, and allows changing the card the account pays with, reading and downloading its invoices, and changing its billing address and its tax ids; nothing else (no subscription is changed or cancelled there: the plan is `PATCH /v1/billing`). Its link back leads to the account's billing page in the console (`<--console-url>#/a/<account id>/billing?back=portal`), or to `--portal-return-url` where the operator names a page; each session is given it. Before the session, the card the account is charged from is set at Stripe as the one its customer's invoices are paid from, which is the card the portal shows. A card the customer adds there is the account's card from then on (Stripe's `customer.updated` and the portal's SetupIntent, at `POST /stripe/webhook`): the next auto reload, renewal and charge of a debt is collected from it; one that is the card of `--cards-per-fingerprint` other accounts already is taken off the customer again, and the account's card is the one it was. A card removed there is the account's card no more. Going back to the free plan is `PATCH /v1/billing` with `plan` `free`: the balance stays the account's (Credits lapse twelve months after they were bought, and are never paid back), and so does its card. An account that owes may call it. An account that is held is refused (403 `account_held`). Takes `admin`. Needs a control plane started with `--stripe-key-file`, and `--console-url` or `--portal-return-url`.",
        "security": [
          {
            "apiKey": [
              "admin"
            ]
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "properties": {}
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The session, made now.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BillingPortal"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_json`, `unknown_field`, `unknown_parameter`: the call takes no field and no parameter.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          },
          "409": {
            "description": "`stripe_not_configured`: the control plane was started without `--stripe-key-file`, or with neither `--console-url` nor `--portal-return-url`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "stripe_not_configured"
            ]
          },
          "502": {
            "description": "`stripe_refused`: Stripe refused the call the control plane made of it; the message has Stripe's words, its code and the id of its request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "stripe_refused"
            ]
          },
          "503": {
            "description": "`stripe_unavailable`: Stripe could not be reached, or was busy or failing. `retryable`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "stripe_unavailable"
            ]
          }
        }
      }
    },
    "/v1/billing/invoices": {
      "get": {
        "operationId": "listInvoices",
        "tags": [
          "Billing"
        ],
        "summary": "The account's invoices at Stripe",
        "description": "Every charge of the account that has an invoice at Stripe (one the collector made, or one a payment page made), the newest charge first, with what Stripe said of the invoice: its number, status and total, and its page and PDF. Stripe's links last some weeks from when they are read (30 days past an invoice's date, and at least 10 days past the moment they were read): the ones the control plane keeps are read again from Stripe when they were read more than a day ago (`links_read_at`), and so is the PDF of a charge's credit note. Where Stripe cannot be asked, what was kept is shown, with when it was read. A charge taken back shows the credit note of its invoice. Takes `read`.",
        "security": [
          {
            "apiKey": [
              "read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of invoices.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InvoiceList"
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`, `invalid_field` (`limit`), `invalid_cursor`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter",
              "invalid_field",
              "invalid_cursor"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          }
        }
      }
    },
    "/v1/billing/charges": {
      "get": {
        "operationId": "listCharges",
        "tags": [
          "Billing"
        ],
        "summary": "The account's charges: its top-ups",
        "description": "What was to be taken from the card of the account of the key's project, the newest first: the top-ups it asked for and those auto reload made, each as it is now (to collect, collected, failed, or taken back). Every project's keys see the same. Takes `read`.",
        "security": [
          {
            "apiKey": [
              "read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of charges.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChargeList"
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`, `invalid_field` (`limit`), `invalid_cursor`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter",
              "invalid_field",
              "invalid_cursor"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          }
        }
      }
    },
    "/v1/billing/notices": {
      "get": {
        "operationId": "listAccountNotices",
        "tags": [
          "Billing"
        ],
        "summary": "The mail the account was sent, and is to be sent",
        "description": "The notices of the account of the key's project, the newest first (control/README.md, 欠费的账户 「通知」, 撤回, 购买的确认): what it owes and what is deleted for it, its payments that failed, what its plan includes nearly or all used, its Credits that lapse, its auto reload that has no card or waits for a consumer's request, the tax of a payment, the acknowledgements of its withdrawals and the confirmations of its purchases, the answers to its requests to have a limit raised (`limit_raised`, `limit_refused`); each with how it stands (`to_send`: to send, or with the sender with no answer yet; `accepted`; `failed`, and made again where it is of a debt that is open, of a withdrawal or of a purchase), when it is due and when it was sent, the address and the words it went with and their language, once they were made (as they are fixed the first time the notice is handed to the sender). Not the notices sent no more (`cancelled`), nor the mails of the console's users, which are a user's. What the console's billing page lists under Mail. Every project's keys see the same. An account that owes may read it, and so may one that is held for its payments. Takes `read`; with the console's session, the account's owners'.",
        "security": [
          {
            "apiKey": [
              "read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of the account's notices.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountNoticeList"
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`, `invalid_field` (`limit`), `invalid_cursor`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter",
              "invalid_field",
              "invalid_cursor"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          }
        }
      }
    },
    "/v1/billing/ledger": {
      "get": {
        "operationId": "listLedger",
        "tags": [
          "Billing"
        ],
        "summary": "The entries the account's balance is the sum of",
        "description": "The ledger of the account of the key's project, the newest entry first: the lots (a top-up that was collected, what the operator granted), what each month's usage past what the plan includes took, what was written off of lots that lapsed, and what was taken with a top-up that was taken back. Their `credits` add up to the balance. Every project's keys see the same. Takes `read`.",
        "security": [
          {
            "apiKey": [
              "read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of entries.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LedgerEntryList"
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`, `invalid_field` (`limit`), `invalid_cursor`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter",
              "invalid_field",
              "invalid_cursor"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          }
        }
      }
    },
    "/v1/keys": {
      "post": {
        "operationId": "createKey",
        "tags": [
          "Keys"
        ],
        "summary": "Make a key",
        "description": "A key of the calling key's project, with the scopes asked for (every one but `admin` where none are), which may run out. The answer has the key itself, once. Takes `admin`.",
        "security": [
          {
            "apiKey": [
              "admin"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/KeyCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The key, with the key itself.",
            "headers": {
              "Location": {
                "schema": {
                  "type": "string"
                },
                "description": "`/v1/keys/{id}`."
              },
              "Idempotent-Replayed": {
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                },
                "description": "Present when the answer is the one of an earlier request with the same `Idempotency-Key`."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KeyMade"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_json`, `unknown_field`, `unknown_parameter`; `invalid_field`: a `name` of more than 100 characters or with a control character in it, a scope that is none of the seven, an `expires_at` that is no time, `admin` for a key held to an installation; `expires_in_the_past`; `invalid_idempotency_key`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter",
              "invalid_field",
              "expires_in_the_past",
              "invalid_idempotency_key"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          },
          "404": {
            "description": "`installation_not_found`: `installation` names none of the project's installations.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "installation_not_found"
            ]
          },
          "409": {
            "description": "`idempotency_key_reused`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "idempotency_key_reused"
            ]
          }
        }
      },
      "get": {
        "operationId": "listKeys",
        "tags": [
          "Keys"
        ],
        "summary": "List the project's keys",
        "description": "Takes `admin`.",
        "security": [
          {
            "apiKey": [
              "admin"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "The keys, the newest first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KeyList"
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`, `invalid_field` (`limit`), `invalid_cursor`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter",
              "invalid_field",
              "invalid_cursor"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          }
        }
      }
    },
    "/v1/keys/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "The key's id (`key-...`).",
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "operationId": "getKey",
        "tags": [
          "Keys"
        ],
        "summary": "A key",
        "description": "Takes `admin`.",
        "security": [
          {
            "apiKey": [
              "admin"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "The key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Key"
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`: the call takes none.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          },
          "404": {
            "description": "`key_not_found`: the project has no such key. Another project's key is not there for it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "key_not_found"
            ]
          }
        }
      },
      "delete": {
        "operationId": "deleteKey",
        "tags": [
          "Keys"
        ],
        "summary": "Revoke a key",
        "description": "At once: the next call that comes with it is 401 `invalid_api_key`. A key may revoke itself. Takes `admin`.",
        "security": [
          {
            "apiKey": [
              "admin"
            ]
          }
        ],
        "responses": {
          "204": {
            "description": "Revoked."
          },
          "400": {
            "description": "`unknown_parameter`: the call takes none.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          },
          "404": {
            "description": "`key_not_found`: the project has no such key. Another project's key is not there for it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "key_not_found"
            ]
          }
        }
      }
    },
    "/v1/limits": {
      "get": {
        "operationId": "getLimits",
        "tags": [
          "Limits"
        ],
        "summary": "The project's spend limit, and what it has spent",
        "description": "Takes `read`.",
        "security": [
          {
            "apiKey": [
              "read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "The project's limit and its month.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Limits"
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`: the call takes none.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          }
        }
      },
      "patch": {
        "operationId": "updateLimits",
        "tags": [
          "Limits"
        ],
        "summary": "Set the project's spend limit, or take it away",
        "description": "Held at the next look: a limit under what is spent freezes the project's sandboxes within a few seconds, and one above it, or none, lets them go, to be woken by the first call that needs them. Takes `admin`.",
        "security": [
          {
            "apiKey": [
              "admin"
            ]
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LimitsUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The project's limit as it is now.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Limits"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_json`, `unknown_field`, `unknown_parameter`; `invalid_field`: `spend_limit_usd` is no number of dollars of zero or more.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter",
              "invalid_field"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          }
        }
      }
    },
    "/v1/limits/requests": {
      "get": {
        "operationId": "listLimitRequests",
        "tags": [
          "Limits"
        ],
        "summary": "The account's requests to have a limit raised, and their answers",
        "description": "The requests of the account of the key's project (`POST /v1/limits/requests`), the newest first, each with how it stands and the operator's answer. Every project's keys see the same. Takes `read`.",
        "security": [
          {
            "apiKey": [
              "read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of the account's requests.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LimitRequestList"
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`, `invalid_field` (`limit`), `invalid_cursor`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter",
              "invalid_field",
              "invalid_cursor"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          }
        }
      },
      "post": {
        "operationId": "createLimitRequest",
        "tags": [
          "Limits"
        ],
        "summary": "Ask for one of the account's limits to be raised",
        "description": "Asks the operator to raise one of the limits that are raised on request (control/README.md, 「请我们调高」): one of the eight caps of what the account has at once and makes in a while, by the name a refusal's `param` (403 `plan_cap_reached`) and `GET /v1/account`'s `caps` give it, or what a project keeps of Drives and volumes: how many (`volumes`) and how large one may be (`volume_bytes`, granted in whole GiB, up to 16 TiB). `wanted` is more than the account's number now, which the request keeps (`from`); `why` says what for. Asking changes nothing: the operator grants the request (the account is given the number as one of its own, which holds on every plan it moves to until it is taken back, and `GET /v1/account` shows it with `source` `operator`) or refuses it, with why, and the account is told by an event of every project (`limit_request.granted`, `limit_request.refused`) and by mail. One request of a limit is open at a time. Takes `admin`.",
        "security": [
          {
            "apiKey": [
              "admin"
            ]
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LimitRequestCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The request, pending.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LimitRequest"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_json`, `unknown_field`, `unknown_parameter`; `missing_field`: no `limit`, `wanted` or `why`; `invalid_field`: `limit` is none of the limits raised on request, `wanted` is no whole number above the account's number now (16 TiB at the most of `volume_bytes`), or `why` is not 1 to 2,000 characters (`param` names which).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter",
              "missing_field",
              "invalid_field"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          },
          "409": {
            "description": "`limit_request_pending`: the account asked for the same limit already, and that request waits for its answer (`param` `limit`; the message names it).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "limit_request_pending"
            ]
          }
        }
      }
    },
    "/v1/limits/requests/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "The request's id.",
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "operationId": "getLimitRequest",
        "tags": [
          "Limits"
        ],
        "summary": "One of the account's requests to have a limit raised",
        "description": "How it stands, and the operator's answer. Takes `read`.",
        "security": [
          {
            "apiKey": [
              "read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "The request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LimitRequest"
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`: the call takes none.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          },
          "404": {
            "description": "`limit_request_not_found`: the account has no such request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "limit_request_not_found"
            ]
          }
        }
      }
    },
    "/v1/sandboxes/{id}/limits": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "The sandbox's id or its name.",
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "operationId": "getSandboxLimits",
        "tags": [
          "Limits"
        ],
        "summary": "A sandbox's own spend limit, and what it has spent",
        "description": "The control plane's: a node alone has no such call. Takes `read`.",
        "security": [
          {
            "apiKey": [
              "read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "The sandbox's limit and its month.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SandboxLimits"
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`: the call takes none.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          },
          "404": {
            "description": "`sandbox_not_found`: the project has no such sandbox.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "sandbox_not_found"
            ]
          }
        }
      },
      "patch": {
        "operationId": "updateSandboxLimits",
        "tags": [
          "Limits"
        ],
        "summary": "Set a sandbox's own spend limit, or take it away",
        "description": "As the project's: held at the next look. Takes `write`, as every setting of a sandbox does.",
        "security": [
          {
            "apiKey": [
              "write"
            ]
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LimitsUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The sandbox's limit as it is now.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SandboxLimits"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_json`, `unknown_field`, `unknown_parameter`; `invalid_field`: `spend_limit_usd` is no number of dollars of zero or more.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter",
              "invalid_field"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          },
          "404": {
            "description": "`sandbox_not_found`: the project has no such sandbox.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "sandbox_not_found"
            ]
          }
        }
      }
    },
    "/v1/events": {
      "get": {
        "operationId": "listEvents",
        "tags": [
          "Events"
        ],
        "summary": "The project's events: a page of them, or a stream",
        "description": "Every event of the project, in the order the control plane recorded them, the newest first, a page at a time. With `Accept: text/event-stream` the answer is a stream that goes on, of the events recorded from then on (or from after the one `Last-Event-ID` names), the oldest first: `event:` is the type, `id:` the event's id, `data:` the event; a line that is no event (`: `) every 15 seconds the stream is quiet. It ends when the reader goes; a control plane that starts again cuts it, and the reader comes back with the last id it saw. An event is recorded from what the nodes show of the project's things, not from the calls that pass: none is missed while the control plane is away, and a sandbox that was made and deleted between two looks has both of its events. An account that owes may read it. Takes `read`.",
        "security": [
          {
            "apiKey": [
              "read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "name": "type",
            "in": "query",
            "description": "Only the events of this type.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "sandbox_id",
            "in": "query",
            "description": "Only the events of this sandbox: its id, or its name while it is there.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Last-Event-ID",
            "in": "header",
            "description": "With the stream: begin after this event, the id of one of the project's events.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of events; or, asked for with `Accept: text/event-stream`, the stream.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EventList"
                }
              },
              "text/event-stream": {
                "schema": {
                  "$ref": "#/components/schemas/Event"
                },
                "description": "Each event's `data:` is an `Event`; its `event:` is its type, its `id:` its id."
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`, `invalid_field` (`limit`; and `Last-Event-ID` that is no event of the project, `param` `Last-Event-ID`), `invalid_cursor`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter",
              "invalid_field",
              "invalid_cursor"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          }
        }
      }
    },
    "/v1/webhooks": {
      "post": {
        "operationId": "createWebhook",
        "tags": [
          "Webhooks"
        ],
        "summary": "Send the project's events to an address",
        "description": "Every event of the project of a type the webhook takes, recorded from now on, is posted to `url` (`webhooks.event`: the request, its headers, its signature and when it is sent again). Takes `admin`.",
        "security": [
          {
            "apiKey": [
              "admin"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The webhook, with its secret.",
            "headers": {
              "Location": {
                "schema": {
                  "type": "string"
                },
                "description": "`/v1/webhooks/{id}`."
              },
              "Idempotent-Replayed": {
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                },
                "description": "Present when the answer is the one of an earlier request with the same `Idempotency-Key`."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookMade"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_json`, `unknown_field`, `unknown_parameter`; `missing_field`: no `url`; `invalid_field`: a `url` that is not such an address, a type that is none, `enabled` that is not true or false; `invalid_idempotency_key`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter",
              "invalid_field",
              "missing_field",
              "invalid_idempotency_key"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          },
          "409": {
            "description": "`idempotency_key_reused`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "idempotency_key_reused"
            ]
          }
        }
      },
      "get": {
        "operationId": "listWebhooks",
        "tags": [
          "Webhooks"
        ],
        "summary": "List the project's webhooks",
        "description": "Takes `admin`.",
        "security": [
          {
            "apiKey": [
              "admin"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "The webhooks, the newest first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookList"
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`, `invalid_field` (`limit`), `invalid_cursor`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter",
              "invalid_field",
              "invalid_cursor"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          }
        }
      }
    },
    "/v1/webhooks/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "The webhook's id (`wh-...`).",
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "operationId": "getWebhook",
        "tags": [
          "Webhooks"
        ],
        "summary": "A webhook, and how its last delivery went",
        "description": "Takes `admin`.",
        "security": [
          {
            "apiKey": [
              "admin"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "The webhook.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Webhook"
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`: the call takes none.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          },
          "404": {
            "description": "`webhook_not_found`: the project has no such webhook.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "webhook_not_found"
            ]
          }
        }
      },
      "patch": {
        "operationId": "updateWebhook",
        "tags": [
          "Webhooks"
        ],
        "summary": "Change a webhook's address, its types, or whether it is enabled",
        "description": "Holds for the events recorded from now on. Takes `admin`.",
        "security": [
          {
            "apiKey": [
              "admin"
            ]
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The webhook as it is now.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Webhook"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_json`, `unknown_field`, `unknown_parameter`; `invalid_field`: as when it is made.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter",
              "invalid_field"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          },
          "404": {
            "description": "`webhook_not_found`: the project has no such webhook.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "webhook_not_found"
            ]
          }
        }
      },
      "delete": {
        "operationId": "deleteWebhook",
        "tags": [
          "Webhooks"
        ],
        "summary": "Delete a webhook",
        "description": "What was still to be sent to it is not sent. Takes `admin`.",
        "security": [
          {
            "apiKey": [
              "admin"
            ]
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted."
          },
          "400": {
            "description": "`unknown_parameter`: the call takes none.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          },
          "404": {
            "description": "`webhook_not_found`: the project has no such webhook.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "webhook_not_found"
            ]
          }
        }
      }
    },
    "/v1/audit": {
      "get": {
        "operationId": "listAudit",
        "tags": [
          "Audit"
        ],
        "summary": "The project's audit log",
        "description": "The entries the store keeps (`--audit-keep-days`, 90 as the control plane is started by default); older ones are written to the object store the backups go to, where there is one, and deleted from the store (`GET /internal/control`, `audit`). Calls refused because the account owes are in it. Takes `admin`.",
        "security": [
          {
            "apiKey": [
              "admin"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "The entries, the newest first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuditList"
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`, `invalid_field` (`limit`), `invalid_cursor`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter",
              "invalid_field",
              "invalid_cursor"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          }
        }
      }
    },
    "/v1/connection": {
      "get": {
        "operationId": "getConnection",
        "tags": [
          "Connected apps"
        ],
        "summary": "The connection of an app's access token",
        "description": "An app's access token (`connectedApp`) asks what it may do: its connection (the app, the account, the projects it covers, the scopes the user granted), the user who connected it, and the token's own scopes the connection still grants and when the token expires. Read with any scope. What the MCP endpoint asks of a token (sandboxd/mcp/src/http.ts). A key's call is refused: a key's own is `GET /v1/account`.",
        "security": [
          {
            "connectedApp": []
          }
        ],
        "responses": {
          "200": {
            "description": "The connection.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TokenConnection"
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`: the call takes none; `project_required` (`x-connection-calls`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter",
              "project_required"
            ]
          },
          "401": {
            "description": "`missing_api_key`: no token; `invalid_api_key`, `expired_api_key`: a key that is none or ran out; `invalid_token`: not an access token this API takes (its signature, its issuer, its audience, its connection's user or app); `token_expired`; `connection_revoked`: its connection is revoked, its user deleted, or no longer a member of the account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "missing_api_key",
              "invalid_api_key",
              "expired_api_key",
              "invalid_token",
              "token_expired",
              "connection_revoked"
            ],
            "headers": {
              "WWW-Authenticate": {
                "schema": {
                  "type": "string",
                  "pattern": "^Bearer error=\""
                },
                "description": "Of an access token's refusal (RFC 6750): `Bearer error=\"invalid_token\"` (or `insufficient_scope` with `scope=`), `error_description`, and `resource_metadata` (the API's protected resource metadata)."
              }
            }
          },
          "403": {
            "description": "`needs_access_token`: a key's call, or a session's; `project_not_granted`, `user_locked`, `insufficient_scope` (`x-connection-calls`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "needs_access_token",
              "project_not_granted",
              "user_locked",
              "insufficient_scope"
            ],
            "headers": {
              "WWW-Authenticate": {
                "schema": {
                  "type": "string",
                  "pattern": "^Bearer error=\""
                },
                "description": "Of an access token's refusal (RFC 6750): `Bearer error=\"invalid_token\"` (or `insufficient_scope` with `scope=`), `error_description`, and `resource_metadata` (the API's protected resource metadata)."
              }
            }
          },
          "503": {
            "description": "`authorization_server_unavailable`: the authorization server's keys could not be read, and none were read before.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "authorization_server_unavailable"
            ]
          }
        }
      }
    },
    "/v1/project": {
      "get": {
        "operationId": "getProject",
        "tags": [
          "Project"
        ],
        "summary": "The project of the key",
        "description": "Its id, its name, the region of its home, and the ranges of its private network. The home is the region of its first service with volumes (the first of that service's `regions`); none has a project without one. The network's ranges are cut out of 10.64.0.0/10 for this project alone, 64 addresses each, when it first needs an address (its first service has one), and another when its own are full: every address of them is one service's or one sandbox's of the project, on whichever node, and no other project has any of them. A sandbox of the project on its network reaches the project's others at their addresses on whichever node they are, where the nodes are in the net between sites (a control plane with a tunnel): each node is told where the project's addresses on the other nodes are, and lets each other node send from its own sandboxes' addresses alone. Takes `read`.",
        "security": [
          {
            "apiKey": [
              "read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "The project.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Project"
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`: the call takes none.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          }
        }
      },
      "patch": {
        "operationId": "updateProject",
        "tags": [
          "Project"
        ],
        "summary": "Change what the project says of itself",
        "description": "`delete_after_idle_days`: after how many days asleep a sandbox of the project is deleted (1 to 3650), or `null` for never. Answered with the project, as `getProject` answers. Takes `admin`.",
        "security": [
          {
            "apiKey": [
              "admin"
            ]
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ProjectUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The project, as it is now.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Project"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_field`: `delete_after_idle_days` is not a whole number of days from 1 to 3650, or null. `unknown_field`, `invalid_json`, `unknown_parameter`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_field",
              "unknown_field",
              "invalid_json",
              "unknown_parameter"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          }
        }
      }
    },
    "/v1/variables": {
      "get": {
        "operationId": "listVariables",
        "tags": [
          "Project"
        ],
        "summary": "List the project's shared variables",
        "description": "By name, with their values. A shared variable is nothing by itself: a service whose `env` refers to it (`${{ shared.REGION }}`) has its value in the versions made after. A version made before keeps the value it was made with. Takes `read`.",
        "security": [
          {
            "apiKey": [
              "read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "The variables, by name.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VariableList"
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`, `invalid_field` (`limit`), `invalid_cursor`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter",
              "invalid_field",
              "invalid_cursor"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          }
        }
      }
    },
    "/v1/variables/{name}": {
      "parameters": [
        {
          "name": "name",
          "in": "path",
          "required": true,
          "description": "The variable's name: 1 to 128 letters, digits and underscores, not beginning with a digit.",
          "schema": {
            "type": "string"
          }
        }
      ],
      "put": {
        "operationId": "putVariable",
        "tags": [
          "Project"
        ],
        "summary": "Put a shared variable",
        "description": "Makes it, or gives it another value. A project has 1000 at the most. The services whose versions would be made of another value because of it (their environments refer to it, or to a service's variable that does) are deployed with it, together, as one deploy of the project (`POST /v1/deploys`, `cause: variables`), which the answer names. Takes `write`.",
        "security": [
          {
            "apiKey": [
              "write"
            ]
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/VariablePut"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The variable, and the deploy it made.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VariablePutResult"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_json`, `unknown_field`, `unknown_parameter`; `invalid_name`; `missing_field`: no `value`; `invalid_field`: a value that is no text, longer than 32 KiB, or with a NUL byte; `dependency_cycle`, `dependency_not_found`, `invalid_reference`: the services the change would deploy could not be deployed (as a deploy of them is refused), and nothing is written. The message says that the variable was not changed, which services refer to it, and why their deploy could not be made, at which of their fields (`services.c`, `services.a.env.X`); `param` is null, as no field of this call is wrong.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter",
              "invalid_name",
              "missing_field",
              "invalid_field",
              "dependency_cycle",
              "dependency_not_found"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "description": "`key_scope`, `needs_api_key`, `account_held` (as for every call); `variable_limit_reached`: the project has 1000, and this is none of them.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "key_scope",
              "needs_api_key",
              "account_held",
              "variable_limit_reached"
            ]
          }
        }
      },
      "delete": {
        "operationId": "deleteVariable",
        "tags": [
          "Project"
        ],
        "summary": "Delete a shared variable",
        "description": "A version made after cannot refer to it (400 `invalid_reference`); the versions made before keep its value. Takes `write`.",
        "security": [
          {
            "apiKey": [
              "write"
            ]
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted."
          },
          "400": {
            "description": "`unknown_parameter`: the call takes none.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          },
          "404": {
            "description": "`variable_not_found`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "variable_not_found"
            ]
          }
        }
      }
    },
    "/v1/compose": {
      "post": {
        "operationId": "importCompose",
        "tags": [
          "Apps"
        ],
        "summary": "Import a Compose file as the project's services",
        "description": "The model is the file as `docker compose config --format json` writes it (each `$` of a value written `$$`), the content of its configs read in and its secrets put as the project's (`PUT /v1/secrets/{name}`, under the name the report gives each) before the call, as `glac compose up` does. Every top-level element and every attribute of every service is `done`, `adapted`, `ignored` or `refused`, with why (control/README.md, 「导入 Compose」); `strict` refuses what is ignored too. A model with anything refused writes nothing: 400 `compose_refused`, the report beside the error. Otherwise each service is an app of the project by its name, made or written with what the file says (what it says nothing of is the app's own), its named volumes volumes of the project made on the node of the project's others, and one project deploy (`cause: compose`) is made of the services whose next version would be made of something else than their current one: the same file again writes nothing and deploys nothing (`deploy: null`). A service the project has and the model does not is kept, and named in `absent`. `dry_run` answers the report and writes nothing. Takes `write`, and `admin` besides where a service is given a secret it does not have.",
        "security": [
          {
            "apiKey": [
              "write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ComposeImport"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The report, and the deploy the import made.",
            "headers": {
              "Idempotent-Replayed": {
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                },
                "description": "Present when the answer is the one an earlier request with the same `Idempotency-Key` was given."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ComposeReport"
                }
              }
            }
          },
          "400": {
            "description": "`compose_refused`: the model has something that is refused, and `report` beside the error says what, why and what to change (`param` is `model`); `invalid_field`: `model` is no object, its `services` are not an object of 1 to 100, `profiles` is no list of names, `dry_run` or `strict` is no boolean; `dependency_cycle`: the services wait for each other in a round, references counted (`param` is `model.services.<name>.depends_on`); `unknown_field`, `invalid_json`, `invalid_idempotency_key`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "compose_refused",
              "invalid_field",
              "dependency_cycle",
              "unknown_field",
              "unknown_parameter",
              "invalid_json",
              "invalid_idempotency_key"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "$ref": "#/components/responses/AppNotAllowed"
          },
          "404": {
            "description": "`secret_not_found`: a secret of the model is not the project's yet (`param` is `model.secrets.<name>`; the message names the project's secret to put).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "secret_not_found"
            ]
          },
          "409": {
            "description": "`idempotency_key_reused`; `app_changed`: an app of the import was changed by other calls while it was written, again and again (`retryable`); `name_taken`: an app of a service's name was made by another call meanwhile.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "idempotency_key_reused",
              "app_changed",
              "name_taken"
            ]
          },
          "413": {
            "description": "`body_too_large`: a model is 4 MiB at the most.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "body_too_large"
            ]
          },
          "503": {
            "description": "A volume of the import could not be made: the code is the node's (`no_capacity`, `not_served_here` where it serves no shared volumes, `node_unavailable`). The apps are not written; the volumes made before it are the project's, and the same call again uses them.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "no_capacity",
              "not_served_here",
              "node_unavailable"
            ]
          }
        }
      }
    },
    "/v1/deploys": {
      "post": {
        "operationId": "createProjectDeploy",
        "tags": [
          "Apps"
        ],
        "summary": "Deploy several of the project's services together",
        "description": "Each service the call names is given what a deploy of it alone would give it (`POST /v1/apps/{app}/versions`: what is given replaces what the app has, the rest is the app's), written into the app at once; every other service whose version would be made of something else because of that, through its references, is in the deploy too. A project deploys one at a time: this one waits while another goes on, and takes the place of one that waits (whose `replaced_by` names it), deploying what that one was for as well. Taken up, it gives a version to each of its services whose version would be made of something else than its current one, runs the jobs (`kind: job`) that what it deploys waits for to have `completed`, and goes through them in the order of what each waits for (`depends_on`, and the services its environment refers to, as `started`): stateful services first, each switched as its instance is up; then jobs, each run to its end in a sandbox of the project's network; then the templates of the rest, built on the project's network; and those switch together, in one transaction, once every one is built (`project.deployed`). A failure ends it there: what switched stays switched, nothing else does (`project.deploy_failed`). Takes `write`; giving a service a secret it does not have takes `admin` besides.",
        "security": [
          {
            "apiKey": [
              "write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ProjectDeployCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The deploy, `queued`.",
            "headers": {
              "Location": {
                "schema": {
                  "type": "string"
                },
                "description": "`/v1/deploys/{id}`."
              },
              "Idempotent-Replayed": {
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                },
                "description": "Present when the answer is the one an earlier request with the same `Idempotency-Key` was given."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProjectDeploy"
                }
              }
            }
          },
          "400": {
            "description": "As for a deploy of one service, its `param` under `services.<name>`; `invalid_field`: `services` is not an object of 1 to 100 services; `missing_field`: a service with no image that the call gives none; `dependency_cycle`: the services of the deploy wait for each other in a round (`param` is `services`); `dependency_not_found`: a service of the deploy waits for one the project does not have (`param` is `services.<name>`); `invalid_reference`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter",
              "invalid_field",
              "invalid_image",
              "invalid_env",
              "missing_field",
              "invalid_idempotency_key",
              "volume_single_instance",
              "invalid_reference",
              "dependency_cycle",
              "dependency_not_found"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "$ref": "#/components/responses/AppNotAllowed"
          },
          "404": {
            "description": "`app_not_found`: the project has no service of a name the call gives (`param` is `services.<name>`); `secret_not_found`; `volume_not_found`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "app_not_found",
              "secret_not_found",
              "volume_not_found"
            ]
          },
          "409": {
            "description": "`idempotency_key_reused`; `volumes_apart`; `app_changed` (`retryable`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "idempotency_key_reused",
              "volumes_apart",
              "app_changed"
            ]
          }
        }
      },
      "get": {
        "operationId": "listProjectDeploys",
        "tags": [
          "Apps"
        ],
        "summary": "The project's deploys",
        "description": "The newest first. Takes `read`.",
        "security": [
          {
            "apiKey": [
              "read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of deploys.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProjectDeployList"
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`, `invalid_field`, `invalid_cursor`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter",
              "invalid_field",
              "invalid_cursor"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          }
        }
      }
    },
    "/v1/deploys/{deploy}": {
      "parameters": [
        {
          "name": "deploy",
          "in": "path",
          "required": true,
          "description": "The deploy's id (`dpl-...`).",
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "operationId": "getProjectDeploy",
        "tags": [
          "Apps"
        ],
        "summary": "One of the project's deploys",
        "description": "Where each of its services stands. With `wait_seconds` the answer comes when the deploy is over (`deployed`, `failed` or `replaced`), or when the time is up: the deploy as it is then. Takes `read`.",
        "security": [
          {
            "apiKey": [
              "read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "wait_seconds",
            "in": "query",
            "description": "For how long to wait for the deploy to end.",
            "schema": {
              "type": "number",
              "minimum": 0,
              "maximum": 3600
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The deploy.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProjectDeploy"
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`, `invalid_field`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter",
              "invalid_field"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          },
          "404": {
            "description": "`deploy_not_found`: the project has no such deploy.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "deploy_not_found"
            ]
          }
        }
      }
    },
    "/v1/volumes/{id}/backups": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "The volume's id or its name; to list the backups of a volume that is gone, its id.",
          "schema": {
            "type": "string"
          }
        }
      ],
      "post": {
        "operationId": "createVolumeBackup",
        "tags": [
          "Backups"
        ],
        "summary": "Back a volume up",
        "description": "A copy of the volume at this instant, taken by its node before the answer (milliseconds: a clone of its file, with the guest's file system frozen around it where a running sandbox has the volume, the node's mount frozen for a shared volume; before it, what the guest and the host had still to write of the volume is written, which a busy host can take seconds over), which the node's uploader then puts in the object store: `making` until it is there, then `ready` (`backup.made`), or `failed` (`backup.failed`). Kept until `expires_in_days` from now, and billed from its `ready_at` for what the store holds of it. A project held back by its spend limit or an account with nothing left makes none (403), nor does an account that owes (402): it is one more thing that costs. Takes `write`.",
        "security": [
          {
            "apiKey": [
              "write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/VolumeBackupCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The backup, `making`.",
            "headers": {
              "Idempotent-Replayed": {
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                },
                "description": "Present when the answer is the one an earlier request with the same `Idempotency-Key` was given."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VolumeBackup"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_json`, `unknown_field`, `unknown_parameter`; `invalid_field`: `expires_in_days` that is not a whole number from 1 to 365; `invalid_idempotency_key`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter",
              "invalid_field",
              "invalid_idempotency_key"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "description": "`key_scope`, `needs_api_key`, `account_held` (`NotAllowed`); `spend_limit_reached`: the project is at its spend limit; `balance_used_up`: what the account's plan includes is used up and it holds nothing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "key_scope",
              "needs_api_key",
              "account_held",
              "spend_limit_reached",
              "balance_used_up"
            ]
          },
          "404": {
            "description": "`volume_not_found`: the project has no such volume.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "volume_not_found"
            ]
          },
          "409": {
            "description": "`not_copied`: the volume's node copies nothing to an object store, and backs nothing up; `volume_restoring`: the volume is still being made from a backup itself, or could not be; `idempotency_key_reused`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "not_copied",
              "volume_restoring",
              "idempotency_key_reused"
            ]
          },
          "503": {
            "description": "`node_unavailable` (`retryable`): the volume's node cannot be reached.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "node_unavailable"
            ]
          }
        }
      },
      "get": {
        "operationId": "listVolumeBackups",
        "tags": [
          "Backups"
        ],
        "summary": "The backups of a volume",
        "description": "The newest first, deleted volumes' by their id. An account that owes may read them, and a project that is held back. Takes `read`.",
        "security": [
          {
            "apiKey": [
              "read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of backups.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VolumeBackupList"
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`, `invalid_field` (`limit`), `invalid_cursor`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter",
              "invalid_field",
              "invalid_cursor"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          }
        }
      }
    },
    "/v1/backups": {
      "get": {
        "operationId": "listProjectBackups",
        "tags": [
          "Backups"
        ],
        "summary": "The project's backups",
        "description": "Every backup of the project's that has not gone, of every volume, those that are gone among them; the newest first. An account that owes may read them, and a project that is held back: a volume is made from one (`POST /v1/volumes` with `from_backup`) all the same. Takes `read`.",
        "security": [
          {
            "apiKey": [
              "read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "name": "volume_id",
            "in": "query",
            "description": "Only the backups of this volume, by its id (of a volume that is gone too).",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of backups.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VolumeBackupList"
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`, `invalid_field` (`limit`), `invalid_cursor`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter",
              "invalid_field",
              "invalid_cursor"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          }
        }
      }
    },
    "/v1/backups/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "The backup's id (`bak-…`).",
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "operationId": "getVolumeBackup",
        "tags": [
          "Backups"
        ],
        "summary": "One of the project's backups",
        "description": "Where it stands: being made, in the object store, or failed. An account that owes may read it. Takes `read`.",
        "security": [
          {
            "apiKey": [
              "read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "The backup.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VolumeBackup"
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          },
          "404": {
            "description": "`backup_not_found`: the project has no such backup (one that was deleted, or that expired, is none).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "backup_not_found"
            ]
          }
        }
      },
      "delete": {
        "operationId": "deleteVolumeBackup",
        "tags": [
          "Backups"
        ],
        "summary": "Delete a backup",
        "description": "It is gone from the lists at once (`backup.deleted`, `why` `owner`), and is billed no further; one that is being made is called off; its node forgets it, and its objects leave the object store once the delete is older than the collector's grace. An account that owes deletes one as it deletes a volume, which stops what it owes from growing by it: unless the control plane is started with `--debt-allows-delete false`, when it is refused 402 `account_in_debt` as the account's other deletes are. Takes `write`.",
        "security": [
          {
            "apiKey": [
              "write"
            ]
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted."
          },
          "400": {
            "description": "`unknown_parameter`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          },
          "404": {
            "description": "`backup_not_found`: the project has no such backup (one that was deleted, or that expired, is none).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "backup_not_found"
            ]
          }
        }
      }
    },
    "/v1/ssh-keys": {
      "post": {
        "operationId": "createSshKey",
        "tags": [
          "SSH keys"
        ],
        "summary": "Register an SSH public key",
        "description": "The node's call (`sandboxd/openapi/openapi.json`), answered by the control plane, which keeps the project's keys for every node; no node hears of them. Whoever registers a key can enter every sandbox of the project it opens: it takes `admin`. Keys of sandboxes that are gone are deleted first. An `Idempotency-Key` is not looked at: the same key again is 409 `ssh_key_exists`.",
        "security": [
          {
            "apiKey": [
              "admin"
            ]
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SshKeyCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The key.",
            "headers": {
              "Location": {
                "schema": {
                  "type": "string"
                },
                "description": "`/v1/ssh-keys/{id}`."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SshKey"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_json`, `unknown_field`, `unknown_parameter`; `invalid_field`: a field of the wrong type, an `expires_at` that is no time; `missing_field`: no `public_key`; `invalid_public_key`: no public key of a type that is taken (the message says what it is instead); `invalid_name`; `expires_in_the_past`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter",
              "invalid_field",
              "missing_field",
              "invalid_public_key",
              "invalid_name",
              "expires_in_the_past"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          },
          "404": {
            "description": "`sandbox_not_found`: no sandbox of the project is what `sandbox_id` names.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "sandbox_not_found"
            ]
          },
          "409": {
            "description": "`name_taken`: a key of the project has the name; `ssh_key_exists`: the key is registered already (the message names it); `too_many_ssh_keys`: the project has 1000.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "name_taken",
              "ssh_key_exists",
              "too_many_ssh_keys"
            ]
          }
        }
      },
      "get": {
        "operationId": "listSshKeys",
        "tags": [
          "SSH keys"
        ],
        "summary": "List the project's SSH keys",
        "description": "Takes `read`.",
        "security": [
          {
            "apiKey": [
              "read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "name": "sandbox_id",
            "in": "query",
            "description": "Only the keys that open this one sandbox alone: its id or its name (a sandbox that is gone by its id).",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The keys, the newest first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SshKeyList"
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`, `invalid_field` (`limit`), `invalid_cursor`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter",
              "invalid_field",
              "invalid_cursor"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          }
        }
      }
    },
    "/v1/ssh-keys/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "The key's id (`sshkey-...`).",
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "operationId": "getSshKey",
        "tags": [
          "SSH keys"
        ],
        "summary": "An SSH key",
        "description": "Takes `read`.",
        "security": [
          {
            "apiKey": [
              "read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "The key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SshKey"
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`: the call takes none.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          },
          "404": {
            "description": "`ssh_key_not_found`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "ssh_key_not_found"
            ]
          }
        }
      },
      "delete": {
        "operationId": "deleteSshKey",
        "tags": [
          "SSH keys"
        ],
        "summary": "Delete an SSH key",
        "description": "It opens nothing from now on, and the sessions that came in by it end within the half minute after which a session asks again. Takes `admin`.",
        "security": [
          {
            "apiKey": [
              "admin"
            ]
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted."
          },
          "400": {
            "description": "`unknown_parameter`: the call takes none.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          },
          "404": {
            "description": "`ssh_key_not_found`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "ssh_key_not_found"
            ]
          }
        }
      }
    },
    "/v1/browsers": {
      "post": {
        "operationId": "createBrowser",
        "tags": [
          "Browsers"
        ],
        "summary": "Make a Browsers session, or have the one of its name",
        "description": "A session is a sandbox of the node's browser image for its `kind` (the image `--browser-images` names, by digest in production), fixed at 1 vCPU and 2 GiB, made where `region` says as any sandbox is, and marked as a Browsers session in the control plane's own table: never by metadata, which no customer can set to make a plain sandbox one (`reserved_metadata`). Its id is its sandbox's: every call of `/v1/sandboxes/{id}/…` works on it, and `DELETE /v1/sandboxes/{id}` ends it. `cdp_url` is the signed address of its port 9222, which any CDP client (Playwright's `connectOverCDP`, Puppeteer's `connect`) takes. It is awake while a client is connected to its browser (or a headful one's screen), whatever its pages do, and falls asleep `sleep_after_seconds` after the last connection closed, with its pages; a connection that says nothing for `connection_idle_seconds` is closed by its node (its sandbox's `awake_ports` and `connection_idle_seconds`). Its egress never reaches ports 25, 465, 587 and the ports mining pools listen on (its rules' `deny_ports`, kept through any change of them). A `name` the project has for a session already answers that session, woken where it slept (200); one a plain sandbox has is refused (409 `name_taken`). On Free a session is held to Free's quotas of the day, of CPU time and memory, as any sandbox is (429 `quota_used_up`). How many are awake at once is held by the plan's caps on what is awake (5 on Free: 5 sandboxes, 10 GiB). A session made on a node that keeps no ready template of its image, which builds one for it (the first session of the image there), is answered once the build is over and its browser runs, with its `ws_url`: it can take as long as the build. Takes an `Idempotency-Key`. Takes `write`. With `profile`, the profile's version (the newest, or `version`) is loaded into the session before it is answered: the browser is started again on it, logged in where it was (a session made with `save: true` saves it when it is deleted). `kind` `lightpanda` is a session of Lightpanda (the image browser-lightpanda), a browser that runs a page's scripts and keeps its DOM and lays out and draws nothing: made, priced and held to the rules as the others are, with its `cdp_url` and `ws_url`; it has no `live_url` and keeps no profile (`kind_unsupported`); it has no layout engine, so its screenshots and PDFs over CDP hold the page's text alone (`chromium-headless` gives the page as it is drawn), and a screencast, which it has nothing for, is refused with an error that names `chromium-headless`.",
        "security": [
          {
            "apiKey": [
              "write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BrowserCreate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The session of the name, which the project had: woken where it slept.",
            "headers": {
              "Location": {
                "schema": {
                  "type": "string"
                },
                "description": "`/v1/browsers/{id}`."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Browser"
                }
              }
            }
          },
          "201": {
            "description": "The session, made.",
            "headers": {
              "Location": {
                "schema": {
                  "type": "string"
                },
                "description": "`/v1/browsers/{id}`."
              },
              "Idempotent-Replayed": {
                "schema": {
                  "type": "string"
                },
                "description": "`true` where the answer is the one the same Idempotency-Key was given."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Browser"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_json`, `unknown_field`, `unknown_parameter`; `invalid_field`: `sleep_after_seconds` from 1 to 3600, `connection_idle_seconds` from 60 to 86400, `delete_after_idle_seconds` from 60 to a year (or null), `metadata` an object of strings; `invalid_name`: `name` is no name; `unknown_kind`: `kind` is not `chromium-headless`, `chromium` or `lightpanda`; `kind_unsupported`: a `profile` asked of a `lightpanda` session, which keeps none (`param` `profile`); `unknown_region`; `reserved_metadata`: `metadata` names the key `product`, which is the platform's; `invalid_proxy`: `proxy` is no proxy of yours (`proxy.url` is no `http://` URL of an HTTP proxy, or names an address no sandbox reaches: `param` `proxy.url`, and the message never says the URL); `invalid_idempotency_key`; `invalid_field`, `unknown_field` of `profile` (`param` `profile.name`, `profile.save`, `profile.version`: a name of 1 to 63 lower-case letters, digits and hyphens); `objects_not_served`: a `profile` is named, and this control plane has no object gateway, where profiles are kept; `invalid_name` also for the name `profiles`, which is the profiles' list.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter",
              "invalid_field",
              "invalid_name",
              "unknown_kind",
              "kind_unsupported",
              "unknown_region",
              "reserved_metadata",
              "invalid_proxy",
              "invalid_idempotency_key",
              "objects_not_served"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "description": "`key_scope`, `needs_api_key`, `account_held` (as everywhere); `proxy_not_on_plan`: a proxy asked for on the free plan, which has none; `plan_cap_reached`: one more awake would pass what the plan lets the account have awake at once (`instances`, `vcpus`, `memory_bytes`), or made in a minute or an hour; `spend_limit_reached`, `balance_used_up`: the project is held back, as for a sandbox. `product_not_open`: Browsers is not open on this platform yet (`--products`): nothing new of it is made.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "key_scope",
              "needs_api_key",
              "account_held",
              "proxy_not_on_plan",
              "plan_cap_reached",
              "spend_limit_reached",
              "balance_used_up",
              "product_not_open"
            ]
          },
          "404": {
            "description": "`profile_not_found`: the `profile` named has no version saved and the session does not save it (`save: true` makes the profile with the session's first save), or it has no `version` of that name (`param` `profile.name`, `profile.version`); also where its archive could not be read where it is kept, and the session made for it was deleted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "profile_not_found"
            ]
          },
          "409": {
            "description": "`name_taken`: a plain sandbox of the project has the name (no sandbox becomes a session), or a session of another kind; `idempotency_key_reused`, `idempotency_in_progress`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "name_taken",
              "idempotency_key_reused",
              "idempotency_in_progress"
            ]
          },
          "503": {
            "description": "`not_served_here`: this control plane was given no image for the kind (`param` `kind`); `no_capacity`: no node has room for it now; `node_unavailable`, `unavailable`: the node chosen for it could not be reached, or did not answer in time; `creation_cancelled`: the session could not be marked as one, and its sandbox was deleted. `session_failed`: its sandbox failed before it ran (its image could not be had, its browser did not come up), and was deleted; `message` has its node's code and words, and no session is left of it. `profile_transfer_failed`: the profile could not be loaded into the session (the object store was not reached from it, or its browser did not come back), and the session made for it was deleted: the same call again makes another. `retryable`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "not_served_here",
              "no_capacity",
              "node_unavailable",
              "unavailable",
              "creation_cancelled",
              "profile_transfer_failed",
              "session_failed"
            ]
          }
        }
      },
      "get": {
        "operationId": "listBrowsers",
        "tags": [
          "Browsers"
        ],
        "summary": "List the project's Browsers sessions",
        "description": "Newest first, a page at a time; each as its sandbox is now (as it was last seen where its node cannot be reached: `status` `unavailable`). `status`, `kind` and `name` filter. An account that owes sees them (unless the control plane is started with `--debt-allows-delete false`). Takes `read`; `cdp_url` and `live_url` take `url` too, and are null without it.",
        "security": [
          {
            "apiKey": [
              "read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "awake",
                "asleep",
                "unavailable",
                "failed"
              ]
            }
          },
          {
            "name": "kind",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "chromium-headless",
                "chromium",
                "lightpanda"
              ]
            }
          },
          {
            "name": "name",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of sessions.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BrowserList"
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`, `invalid_field` (`limit`, `status`), `unknown_kind`, `invalid_cursor`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter",
              "invalid_field",
              "unknown_kind",
              "invalid_cursor"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          }
        }
      }
    },
    "/v1/browsers/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          },
          "description": "The session's id (its sandbox's) or its name."
        }
      ],
      "get": {
        "operationId": "getBrowser",
        "tags": [
          "Browsers"
        ],
        "summary": "A Browsers session",
        "description": "As its sandbox is now, with its addresses. An account that owes reads it (unless the control plane is started with `--debt-allows-delete false`). Takes `read`; `cdp_url` and `live_url` take `url` too, and are null without it.",
        "security": [
          {
            "apiKey": [
              "read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "The session.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Browser"
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          },
          "404": {
            "description": "`browser_not_found`: the project has no Browsers session of this id or name (a plain sandbox is none, whatever it runs).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "browser_not_found"
            ]
          }
        }
      },
      "patch": {
        "operationId": "updateBrowser",
        "tags": [
          "Browsers"
        ],
        "summary": "Change a Browsers session",
        "description": "Its sleep after so long without connections or requests, how long a silent connection is kept, its deletion after so long asleep, its metadata (replaced whole): its sandbox's, changed on its node. And its own proxy (BR11), another one or none, on a running session as on a sleeping one: its browser is not started again. Takes `write`.",
        "security": [
          {
            "apiKey": [
              "write"
            ]
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BrowserUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The session as it is now.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Browser"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_json`, `unknown_field`, `unknown_parameter`, `invalid_field` (as at its create), `reserved_metadata`; `invalid_proxy`: `proxy` as at its create.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter",
              "invalid_field",
              "reserved_metadata",
              "invalid_proxy"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "description": "`key_scope`, `needs_api_key`, `account_held` (as everywhere); `proxy_not_on_plan`: a proxy given on the free plan, which has none.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "key_scope",
              "needs_api_key",
              "account_held",
              "proxy_not_on_plan"
            ]
          },
          "404": {
            "description": "`browser_not_found`: the project has no Browsers session of this id or name (a plain sandbox is none, whatever it runs).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "browser_not_found"
            ]
          }
        }
      },
      "delete": {
        "operationId": "deleteBrowser",
        "tags": [
          "Browsers"
        ],
        "summary": "End a Browsers session",
        "description": "Its sandbox deleted, as `DELETE /v1/sandboxes/{id}` deletes it. A session made with a profile to save (`profile.save`) saves it first, as a new version of the profile, unless `?save_profile=false`: a save that does not succeed is the call's refusal, and the session is left as it is. The usage booked to it stays a session's. An account that owes may (unless the control plane is started with `--debt-allows-delete false`). Takes `write`.",
        "security": [
          {
            "apiKey": [
              "write"
            ]
          }
        ],
        "responses": {
          "204": {
            "description": "Ended."
          },
          "400": {
            "description": "`unknown_parameter`; `invalid_field`: `save_profile` is not `true` or `false`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter",
              "invalid_field"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          },
          "404": {
            "description": "`browser_not_found`: the project has no Browsers session of this id or name (a plain sandbox is none, whatever it runs).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "browser_not_found"
            ]
          },
          "409": {
            "description": "`profile_busy`: its profile is being saved by another call now. `retryable`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "profile_busy"
            ]
          },
          "503": {
            "description": "`profile_transfer_failed`: its profile could not be saved (the object store was not reached from it); the session is as it was. `node_unavailable`: its node could not be reached. `retryable`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "profile_transfer_failed",
              "node_unavailable"
            ]
          }
        },
        "parameters": [
          {
            "name": "save_profile",
            "in": "query",
            "schema": {
              "type": "boolean"
            },
            "description": "`false`: a session made with a profile to save (`save: true`) is ended without saving it. True when not given."
          }
        ]
      }
    },
    "/v1/browsers/{id}/sleep": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          },
          "description": "The session's id (its sandbox's) or its name."
        }
      ],
      "post": {
        "operationId": "sleepBrowser",
        "tags": [
          "Browsers"
        ],
        "summary": "Put a Browsers session to sleep now",
        "description": "With its memory and its disk: its pages are there when it wakes. A CDP connection that is open stays open, and its next message wakes it. Takes `write`.",
        "security": [
          {
            "apiKey": [
              "write"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "The session, asleep.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Browser"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_json`, `unknown_field`, `unknown_parameter`: the call takes no body.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          },
          "404": {
            "description": "`browser_not_found`: the project has no Browsers session of this id or name (a plain sandbox is none, whatever it runs).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "browser_not_found"
            ]
          }
        }
      }
    },
    "/v1/browsers/{id}/rotate-url": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          },
          "description": "The session's id (its sandbox's) or its name."
        }
      ],
      "post": {
        "operationId": "rotateBrowserUrl",
        "tags": [
          "Browsers"
        ],
        "summary": "Give a Browsers session new addresses",
        "description": "A new secret for the signed addresses of its ports (the node's `POST /v1/sandboxes/{id}/url-secret`): every address given out before is refused from now on (401 `invalid_link`), the connections they let in that are open (its CDP clients') are ended before the call answers, and the answer has the new ones. Takes `write`.",
        "security": [
          {
            "apiKey": [
              "write"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "The session, with its new addresses.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Browser"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_json`, `unknown_field`, `unknown_parameter`: the call takes no body.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          },
          "404": {
            "description": "`browser_not_found`: the project has no Browsers session of this id or name (a plain sandbox is none, whatever it runs).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "browser_not_found"
            ]
          }
        }
      }
    },
    "/v1/browsers/{id}/live": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          },
          "description": "The session's id (its sandbox's) or its name."
        }
      ],
      "get": {
        "operationId": "getBrowserLive",
        "tags": [
          "Browsers"
        ],
        "summary": "Where a Browsers session is watched live",
        "description": "The session's page as it is drawn, live, at a signed address (as its CDP address is: rotated with it by `rotate-url`). Interactive (the default): its `live_url`, where the viewer's mouse and keyboard reach the page (a headless session's `/live` on its CDP port, the image's page drawing the tab's screencast; a headful one's screen, noVNC). `?interactive=false`: the address of the image's read-only port (6081), signed for that port alone, where only what draws the screen reaches the browser, and keys, clicks, pastes and resizes do not (to put in a page of one's own for people to watch). A viewer connected keeps the session awake as a CDP client does. Takes `url`: the address is a credential.",
        "security": [
          {
            "apiKey": [
              "url"
            ]
          }
        ],
        "parameters": [
          {
            "name": "interactive",
            "in": "query",
            "schema": {
              "type": "boolean"
            },
            "description": "`false`: the read-only address. True when not given."
          }
        ],
        "responses": {
          "200": {
            "description": "Where it is watched.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BrowserLive"
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`; `invalid_field`: `interactive` is not `true` or `false`; `kind_unsupported`: the session is a `lightpanda` one, which has nothing to stream and no live view.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter",
              "invalid_field",
              "kind_unsupported"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          },
          "404": {
            "description": "`browser_not_found`: the project has no Browsers session of this id or name (a plain sandbox is none, whatever it runs).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "browser_not_found"
            ]
          },
          "503": {
            "description": "`unavailable`: its node gives no address now (it is out of reach, or has no preview domain). `retryable`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unavailable"
            ]
          }
        }
      }
    },
    "/v1/browsers/{id}/profile": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          },
          "description": "The session's id (its sandbox's) or its name."
        }
      ],
      "post": {
        "operationId": "saveBrowserProfile",
        "tags": [
          "Browsers"
        ],
        "summary": "Save a Browsers session's profile now",
        "description": "The session's browser profile (its cookies, Local Storage, IndexedDB, Session Storage and preferences) saved as a new version of the profile named, or of the one it was made with: the newest, which sessions made with the profile load from now on. Kept in a bucket the platform keeps for the project behind its object gateway, `glac-profiles-<project>` (made the first time), sealed with the project's key. The browser is stopped as it is closed, so that it writes what it holds, and started again: a client connected is disconnected, and connects again. One save of a profile at a time. Takes `write`.",
        "security": [
          {
            "apiKey": [
              "write"
            ]
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BrowserProfileSave"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The profile, with the version saved first among its versions.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BrowserProfile"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_json`, `unknown_field`, `unknown_parameter`; `invalid_field`: `name` is no profile's name, or none is given for a session made without a profile; `objects_not_served`: this control plane has no object gateway, where profiles are kept; `kind_unsupported`: the session is a `lightpanda` one, which keeps no profile.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter",
              "invalid_field",
              "objects_not_served",
              "kind_unsupported"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          },
          "404": {
            "description": "`browser_not_found`: the project has no Browsers session of this id or name (a plain sandbox is none, whatever it runs).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "browser_not_found"
            ]
          },
          "409": {
            "description": "`profile_busy`: the profile is being saved by another call now (another session's, or this one's). `retryable`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "profile_busy"
            ]
          },
          "503": {
            "description": "`profile_transfer_failed`: the profile could not be saved (the object store was not reached from the session, or its browser did not come back); `node_unavailable`: its node could not be reached. `retryable`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "profile_transfer_failed",
              "node_unavailable"
            ]
          }
        }
      }
    },
    "/v1/browsers/profiles": {
      "get": {
        "operationId": "listBrowserProfiles",
        "tags": [
          "Browsers"
        ],
        "summary": "List the project's browser profiles",
        "description": "Every profile the project has saved and not deleted, in the order of their names, each with its versions; a page of `limit` (100, at most 1000) at a time, after `cursor`. Takes `read`.",
        "security": [
          {
            "apiKey": [
              "read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "The profiles.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BrowserProfileList"
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`; `invalid_field`: `limit` is not from 1 to 1000; `invalid_cursor`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter",
              "invalid_field",
              "invalid_cursor"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          }
        },
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ]
      }
    },
    "/v1/browsers/profiles/{name}": {
      "parameters": [
        {
          "name": "name",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          },
          "description": "The profile's name."
        }
      ],
      "get": {
        "operationId": "getBrowserProfile",
        "tags": [
          "Browsers"
        ],
        "summary": "Get a browser profile",
        "description": "The profile and its versions, the newest first. Takes `read`.",
        "security": [
          {
            "apiKey": [
              "read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "The profile.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BrowserProfile"
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          },
          "404": {
            "description": "`profile_not_found`: the project has no profile of this name.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "profile_not_found"
            ]
          }
        }
      },
      "delete": {
        "operationId": "deleteBrowserProfile",
        "tags": [
          "Browsers"
        ],
        "summary": "Delete a browser profile",
        "description": "Every version of it, gone from the API at once; their archives are deleted 30 days later. A profile saved again under the name is a new one. Takes `write`.",
        "security": [
          {
            "apiKey": [
              "write"
            ]
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted."
          },
          "400": {
            "description": "`unknown_parameter`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          },
          "404": {
            "description": "`profile_not_found`: the project has no profile of this name.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "profile_not_found"
            ]
          },
          "409": {
            "description": "`profile_busy`: it is being saved now. `retryable`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "profile_busy"
            ]
          }
        }
      }
    },
    "/v1/catalogue": {
      "get": {
        "operationId": "listCatalogue",
        "tags": [
          "Managed Applications"
        ],
        "summary": "List the catalogue's apps",
        "description": "Each app of the catalogue of Managed Applications (reports/platform/managed-apps.md, section 2.6): its newest version that is not withdrawn, as an entry: its title, publisher (`verified`: the platform's own packaging), licence, the versions there are, its parameters' schema, its services, buckets, requirements, outputs, API and calculator. Every app is billed by the Cube resources an installation of it uses, and nothing else. Takes `read`.",
        "security": [
          {
            "apiKey": [
              "read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "The catalogue, its apps by their names, a page of `limit` at a time.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CatalogueList"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_field`: `limit` is no number from 1 to 1000. `invalid_cursor`. `unknown_parameter`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_field",
              "invalid_cursor",
              "unknown_parameter"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          }
        }
      }
    },
    "/v1/catalogue/{app}": {
      "parameters": [
        {
          "name": "app",
          "in": "path",
          "required": true,
          "description": "The app's name in the catalogue (`rivet`).",
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "operationId": "getCatalogueApp",
        "tags": [
          "Managed Applications"
        ],
        "summary": "Read an app of the catalogue",
        "description": "An app of the catalogue as its entry has it, with its definition (`definition`: the file as it was published, YAML read as JSON: reports/platform/managed-apps.md, section 2.2): its newest version that is not withdrawn, or the one `version` names (a withdrawn one too). Takes `read`.",
        "security": [
          {
            "apiKey": [
              "read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "version",
            "in": "query",
            "required": false,
            "description": "A version of the app's definition; left out: its newest that is not withdrawn.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The app's entry, with its definition.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CatalogueApp"
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          },
          "404": {
            "description": "`app_not_found`: the catalogue has no such app, or no such version of it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "app_not_found"
            ]
          }
        }
      }
    },
    "/v1/installations": {
      "post": {
        "operationId": "createInstallation",
        "tags": [
          "Managed Applications"
        ],
        "summary": "Install an app of the catalogue",
        "description": "An installation of an app of the catalogue in the project (reports/platform/managed-apps.md, section 2.3): its `app`, `name` (the app's name where none is given), `region` (the control plane's own where none is), `version` (the newest that is not withdrawn where none is) and `parameters`, held to the app's schema, each the schema does not name refused and each left out at its default. Its parts are made through the platform's own API in the project's name, as the project's own calls would make them, and each is noted as it is made: what the app requires (another app of the catalogue, made with it as an installation of its own named `<name>-<requirement>`, or one of the project's installations of that app it is bound to, `requires`); its buckets (a prefix of the platform's bucket of the project in its region, reached by a key for that prefix alone, billed as volume data); then each service, an app of the project named `<name>-<service>`: its volumes first (Drives, `<app>-<volume>`), its secrets (secrets of the project, `INS_<id>_<SERVICE>_<VARIABLE>`: a generated one made once and kept), then the app, labelled `installation_id` and `installation_app` (its instances carry the labels, so that what they use is grouped by installation in the books: `GET /v1/usage?group_by=metadata.installation_id`). Those apps and Drives are listed by `/v1/apps` and `/v1/drives` with `managed_by` `installation`, and are changed through this API alone. A refusal of any part (the plan's limits, a name an app or a Drive has, a node away) is the create's, and undoes what was made: the name is free again. The call waits for its apps' first versions and its app's controller, where it has one, to take it (150 s at the most) and answers the installation `ready`, or still `installing` (`GET` it): the tending loop takes it to `ready`, or to `failed` with why. Its outputs are all in this answer, the secret ones too (`secret_outputs` names them); a read shows those to a key that may manage the project (`admin`) alone. Idempotent by name: a create that names an installation of the project answers that one, 200, where it asks for what it is. Takes `write`.",
        "security": [
          {
            "apiKey": [
              "write"
            ]
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/InstallationCreate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The installation the name names, as the create asks for it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Installation"
                }
              }
            },
            "headers": {
              "Location": {
                "schema": {
                  "type": "string"
                },
                "description": "`/v1/installations/{id}`."
              }
            }
          },
          "201": {
            "description": "The installation, `ready` (or `installing`), with every output.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Installation"
                }
              }
            },
            "headers": {
              "Location": {
                "schema": {
                  "type": "string"
                },
                "description": "`/v1/installations/{id}`."
              }
            }
          },
          "400": {
            "description": "`invalid_json`, `unknown_field`, `unknown_parameter`; `missing_field`: no `app`; `invalid_name`: a name that is not 1 to 24 lower-case letters, digits and hyphens beginning with a letter; `unknown_region`; `invalid_parameter`: a parameter that does not fit the app's schema, or one it does not name (`param` names it: `parameters.size.vcpus`), or a value the app's services cannot be made with; `invalid_field`: `requires` is no object of names; `requirement_mismatch`: `requires` binds an installation of another app; `buckets_not_served`: the app keeps a bucket, and this platform keeps none here; and what a part's create was refused for (`invalid_field` and the like, of an app or a Drive).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter",
              "missing_field",
              "invalid_name",
              "unknown_region",
              "invalid_parameter",
              "invalid_field",
              "requirement_mismatch",
              "buckets_not_served"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "description": "`key_scope`, `needs_api_key`, `account_held` (as everywhere); `product_not_open`: Managed Applications are not open on this platform yet (`--products`); and what a part's create was refused for: `app_limit_reached`, `env_limit_reached`, `plan_size_exceeded`, `plan_cap_reached`, `drive_limit_reached`, `spend_limit_reached`, `balance_used_up` (the plan's and the account's limits, as an app's or a Drive's own create is refused).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "key_scope",
              "needs_api_key",
              "account_held",
              "product_not_open",
              "app_limit_reached",
              "env_limit_reached",
              "plan_size_exceeded",
              "plan_cap_reached",
              "drive_limit_reached",
              "spend_limit_reached",
              "balance_used_up"
            ]
          },
          "404": {
            "description": "`app_not_found`: the catalogue has no such app, or no such version of it (`param` `version`); `installation_not_found`: `requires` binds an installation the project does not have.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "app_not_found",
              "installation_not_found"
            ]
          },
          "409": {
            "description": "`name_taken`: the project has an installation of the name that is not what the create asks for, or an app or a Drive has a name a part would have; `version_withdrawn`: the version named is withdrawn from the catalogue; `requires_too_deep`: what it requires requires it again, or apps more than three deep.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "name_taken",
              "version_withdrawn",
              "requires_too_deep"
            ]
          },
          "503": {
            "description": "`no_capacity` (`retryable`): no node has room for one of its Drives.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "no_capacity"
            ]
          }
        }
      },
      "get": {
        "operationId": "listInstallations",
        "tags": [
          "Managed Applications"
        ],
        "summary": "List the project's installations",
        "description": "The newest first, a page of `limit` (100, at most 1000) after `cursor` (the last id of the page before); `app` those of one app of the catalogue. Outputs and parameters that are secret are shown to a key that may manage the project (`admin`) alone. Takes `read`.",
        "security": [
          {
            "apiKey": [
              "read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "name": "app",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Only the installations of this app of the catalogue."
          }
        ],
        "responses": {
          "200": {
            "description": "A page of installations.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InstallationList"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_field`: `limit` is no number from 1 to 1000. `invalid_cursor`. `unknown_parameter`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_field",
              "invalid_cursor",
              "unknown_parameter"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          }
        }
      }
    },
    "/v1/installations/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "The installation's id (`ins-…`) or its name.",
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "operationId": "getInstallation",
        "tags": [
          "Managed Applications"
        ],
        "summary": "Read an installation",
        "description": "By its id or its name: how it stands, its parameters, its outputs, and what it is made of (its apps, Drives, buckets with what they held when they were last measured, and what it requires). Outputs and parameters that are secret are shown to a key that may manage the project (`admin`) alone. Takes `read`.",
        "security": [
          {
            "apiKey": [
              "read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "The installation.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Installation"
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          },
          "404": {
            "description": "`installation_not_found`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "installation_not_found"
            ]
          }
        }
      },
      "patch": {
        "operationId": "updateInstallation",
        "tags": [
          "Managed Applications"
        ],
        "summary": "Change an installation's parameters or version",
        "description": "`parameters` is a JSON Merge Patch of what it has (null takes a parameter back to its default), `version` another version of its app (not withdrawn). The installation is worked out again and what changed is made so: an app whose body changed is changed (`PATCH /v1/apps/{app}`, a new version where what a version is made of changed, as a project deploy does), a new service, volume or bucket is made, one that went is taken apart; a generated secret is kept. It is `updating` until its apps' versions are ready (the call waits as a create does), then `ready`, or `failed` with why. Takes `write`.",
        "security": [
          {
            "apiKey": [
              "write"
            ]
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/InstallationUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The installation, `ready` or `updating`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Installation"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_json`, `unknown_field`, `unknown_parameter`; `invalid_parameter`: a parameter that does not fit the app's schema (`param` names it).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter",
              "invalid_parameter"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          },
          "404": {
            "description": "`installation_not_found`; `app_not_found`: no such version of its app (`param` `version`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "installation_not_found",
              "app_not_found"
            ]
          },
          "409": {
            "description": "`installation_deleting`: it is being deleted; `version_withdrawn`; `volume_size_fixed`: a volume's size would change (a Volume's does not).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "installation_deleting",
              "version_withdrawn",
              "volume_size_fixed"
            ]
          }
        }
      },
      "delete": {
        "operationId": "deleteInstallation",
        "tags": [
          "Managed Applications"
        ],
        "summary": "Delete an installation",
        "description": "Its controller told first, then every part taken apart, the newest first: its apps (their instances stop), its Drives once no instance has them, its secrets, its buckets with what they hold (booked as volume data up to then), what it required that was made for it (not an installation it was bound to), and the keys held to it. What a node cannot be reached for is left to the tending loop, the installation `deleting` meanwhile. Takes `write`.",
        "security": [
          {
            "apiKey": [
              "write"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "The installation, `deleted` or `deleting`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Installation"
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          },
          "404": {
            "description": "`installation_not_found`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "installation_not_found"
            ]
          },
          "409": {
            "description": "`required_by_installation`: it was made for another installation, which requires it: it goes with that one.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "required_by_installation"
            ]
          }
        }
      }
    },
    "/v1/managed/rivet/{installation}/health": {
      "get": {
        "operationId": "getRivetHealth",
        "tags": [
          "Managed Applications"
        ],
        "summary": "Ask an installation of Rivet how its engine is",
        "description": "Rivet's own API, answered by the platform for an installation of it (the app's controller, `builtin:rivet`): the engine's own answer to `GET /health`, asked on its instance, which it wakes. Each app of the catalogue with an API of its own has it under `/v1/managed/{app}` (its `api.base`); Rivet's is this call. Takes `read`.",
        "security": [
          {
            "apiKey": [
              "read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "installation",
            "in": "path",
            "required": true,
            "description": "The installation's id (`ins-…`) or its name.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "What the engine said.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RivetHealth"
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          },
          "404": {
            "description": "`installation_not_found`: the project has no installation of the id or name, or it is not of Rivet.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "installation_not_found"
            ]
          },
          "503": {
            "description": "`engine_not_ready` (`retryable`): the engine did not answer, or its instance could not be reached.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "engine_not_ready"
            ]
          }
        }
      }
    },
    "/v1/databases": {
      "post": {
        "operationId": "createDatabase",
        "tags": [
          "Databases"
        ],
        "summary": "Make a database",
        "description": "A managed Postgres (reports/databases/README.md): a Drive (the data; in the strict tier unless `storage.durability` says `standard`: a commit returns once the object store has it) and an app of one stateful service whose instance mounts it, of the platform's Postgres image of the major version (`ghcr.io/glacai/postgres-<version>`), on the project's network under the database's name. The image makes the cluster on the empty Drive: the role group `glac_superuser` (the rights of Neon's `neon_superuser`; no superuser), the owner `<database_name>_owner` in it, the database; the owner's password is set once PostgreSQL is ready, as a SCRAM verifier, and is in this answer and kept nowhere. The call waits for the first start (150 s at the most) and answers the database `ready` (its instance's first start is over: PostgreSQL takes connections, the database is there, the owner has the password), or `creating` (it goes on: `GET` it). Idempotent by name: a create that names a database of the project answers that one, 200, without its password, where it asks for what it is. The app and the Drive are listed by `/v1/apps` and `/v1/drives` with `managed_by`, and changed through this API alone. A refusal of the Drive's or the app's create (the plan's limits, a name an app has) is the create's, and undoes what was made. Takes `write`. With `tiering.enabled` (a paid plan, where the object gateway serves), its branches load pg_lake and `glac_tiering` and run pgduck_server; where their partitions go and the key that reaches that prefix alone are handed to each running machine, never put in its start.",
        "security": [
          {
            "apiKey": [
              "write"
            ]
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DatabaseCreate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The database the name names, as the create asks for it: without its password.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Database"
                }
              }
            },
            "headers": {
              "Location": {
                "schema": {
                  "type": "string"
                },
                "description": "`/v1/databases/{id}`."
              }
            }
          },
          "201": {
            "description": "The database, `ready` (or `creating`, its first start under way), with the owner's password and the private connection string with it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Database"
                }
              }
            },
            "headers": {
              "Location": {
                "schema": {
                  "type": "string"
                },
                "description": "`/v1/databases/{id}`."
              }
            }
          },
          "400": {
            "description": "`invalid_json`; `unknown_field` (of a later slice too: `suspend_after_seconds`, DB9; `allowed_ips`, DB3); `missing_field`: no `name`; `invalid_name`; `unknown_version`: a major version that is not offered (16, 17, 18); `invalid_field` (`engine` other than `postgres`, `database_name`, `size`, `storage`, `sleep_after_seconds`, `metadata`; `history_seconds` not 0 to 2592000, or not 86400 on Free); `unknown_region`; `unknown_parameter`. `invalid_field` of `tiering.enabled` (true or false), `unknown_field` of `tiering.…`. `objects_not_served`: `tiering.enabled` where this control plane has no object gateway.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_field",
              "invalid_json",
              "invalid_name",
              "missing_field",
              "objects_not_served",
              "unknown_field",
              "unknown_parameter",
              "unknown_region",
              "unknown_version"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "description": "`key_scope`, `needs_api_key`, `account_held` (as everywhere); `product_not_open`: Serverless Database is not open on this platform yet (`--products`): nothing new of it is made. `plan_cap_reached`: the free plan's databases (`param`): `databases`, one more than the account may have (100); `storage.size_bytes`, one that would hold more than 1 GB (a free database's Drive is 1 GB, given where the create gives no size); `account_database_cu_hours`, the account's databases computed the free plan's 300 CU-hours this calendar month. `paid_plan_required`: `tiering.enabled` on the free plan.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "account_held",
              "key_scope",
              "needs_api_key",
              "paid_plan_required",
              "plan_cap_reached",
              "product_not_open"
            ]
          },
          "409": {
            "description": "`name_taken`: the project has a database of the name that is not what the create asks for, or an app or a Drive has the name (the Drive is `<name>-data`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "name_taken"
            ]
          },
          "503": {
            "description": "`no_capacity` (`retryable`): no node has room for the Drive, or none copies Drives to an object store (the strict tier).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "no_capacity"
            ]
          }
        }
      },
      "get": {
        "operationId": "listDatabases",
        "tags": [
          "Databases"
        ],
        "summary": "List the project's databases",
        "description": "The newest first, a page of `limit` (100, at most 1000) after `cursor`; `name` finds one by its name. Takes `read`.",
        "security": [
          {
            "apiKey": [
              "read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "name": "name",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Only the database of this name."
          }
        ],
        "responses": {
          "200": {
            "description": "A page of databases, without their passwords.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DatabaseList"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_field`: `limit` is no number from 1 to 1000. `invalid_cursor`. `unknown_parameter`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_field",
              "invalid_cursor",
              "unknown_parameter"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          }
        }
      }
    },
    "/v1/databases/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          },
          "description": "The database's id (`db-…`) or its name."
        }
      ],
      "get": {
        "operationId": "getDatabase",
        "tags": [
          "Databases"
        ],
        "summary": "A database",
        "description": "By its id or its name; its root branch as its instance's node shows it now (`state`). Takes `read`.",
        "security": [
          {
            "apiKey": [
              "read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "The database, without its password.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Database"
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          },
          "404": {
            "description": "`database_not_found`: the project has no database of this id or name (a deleted one is none).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "database_not_found"
            ]
          }
        }
      },
      "delete": {
        "operationId": "deleteDatabase",
        "tags": [
          "Databases"
        ],
        "summary": "Delete a database",
        "description": "Its app is deleted (its instance stops PostgreSQL with a fast shutdown), a snapshot of its Drive at rest is kept seven days (a Drive snapshot of the project's: `GET /v1/drive-snapshots?drive_deleted=true`, forked or mounted as any) unless `final_snapshot=false`, and its Drive is deleted; its branches of its own first, the newest first, each with its app and its Drive. The name is free at once. Takes `write`.",
        "security": [
          {
            "apiKey": [
              "write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "final_snapshot",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "true",
                "false"
              ]
            },
            "description": "`false`: no snapshot is kept. Default `true`."
          }
        ],
        "responses": {
          "200": {
            "description": "The database as it ends: `deleted`, with the snapshot kept (`final_snapshot`); or `deleting` where what is left takes longer, which the control plane finishes.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Database"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_field`: `final_snapshot` is neither `true` nor `false`. `unknown_parameter`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_field",
              "unknown_parameter"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          },
          "404": {
            "description": "`database_not_found`: the project has no database of this id or name (a deleted one is none).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "database_not_found"
            ]
          },
          "409": {
            "description": "`database_busy` (`retryable`): a branch of it is being made: ask again once it is ready.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "database_busy"
            ]
          }
        }
      },
      "patch": {
        "operationId": "updateDatabase",
        "tags": [
          "Databases"
        ],
        "summary": "Change a database",
        "description": "`history_seconds`: how far back its root branch can be restored (slice DB8), held to the plan as on a create; its root branch's Drive's window is set to it at once. Tiered storage turned on or off (`tiering.enabled`, slice DB18), as Tiger's console and `disable_tiering` do: on, each branch's app is deployed again with it (its PostgreSQL restarts once, a few seconds), and its machine is handed where its partitions go (`<database id>/tiered/` of the project's platform bucket, `glac-db-<region>-<project>`) and a key that reaches that prefix alone; off, each branch is asked (and woken where it sleeps) whether it has partitions tiered or queued, which refuses it, and the key goes. Nothing changes where it is as asked already. Its rest (slices DB9 and DB10): how long each branch's instance stays paused before its memory is saved and it sleeps (`suspend_after_seconds`), and the time of day in UTC at which each is woken while it rests (`wake_daily_at`), as on a create: every branch's app is changed at once (its node takes it without a restart), and a branch made from then on has it too. Asked with the others in one call, tiering is turned first, then the rest, then the history; a call refused for what it says changes nothing. Takes `write`.",
        "security": [
          {
            "apiKey": [
              "write"
            ]
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DatabaseUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The database, without its password.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Database"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_json`, `unknown_parameter`; `unknown_field`: a field it does not take, or one a later slice changes (the message says which); `invalid_field`: `history_seconds` is not 0 to 2592000, or not 86400 on Free, `suspend_after_seconds` is not 1 to 86400, `wake_daily_at` is not `HH:MM`, or `tiering` or `tiering.enabled` is not what they take; `objects_not_served`: tiering turned on where this control plane has no object gateway.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "invalid_field",
              "objects_not_served",
              "unknown_parameter"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "description": "`key_scope`, `needs_api_key`, `account_held` (as every call: `x-error-codes`); `paid_plan_required`: tiering turned on on the free plan.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "key_scope",
              "needs_api_key",
              "account_held",
              "paid_plan_required"
            ],
            "$ref": "#/components/responses/NotAllowed"
          },
          "404": {
            "description": "`database_not_found`: the project has no database of this id or name (a deleted one is none).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "database_not_found"
            ]
          },
          "409": {
            "description": "`tiered_data_exists`: tiering turned off while a branch has partitions tiered or queued (the message says which); `database_busy` (`retryable`): the database or a branch of it is being made or deleted, or tended to now; `database_failed`: its first start failed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "tiered_data_exists",
              "database_busy",
              "database_failed"
            ]
          },
          "503": {
            "description": "`database_unavailable` (`retryable`): a branch's machine did not answer whether it has partitions tiered; `node_unavailable` (`retryable`): the node of a branch to be woken is not reached.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "database_unavailable",
              "node_unavailable"
            ]
          }
        }
      }
    },
    "/v1/databases/{id}/branches": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          },
          "description": "The database's id (`db-…`) or its name."
        }
      ],
      "get": {
        "operationId": "listDatabaseBranches",
        "tags": [
          "Databases"
        ],
        "summary": "A database's branches",
        "description": "Its root branch, `main`, first; then its branches of its own that are not deleted (slice DB6), the oldest first: a branch before the branches made of it. Takes `read`.",
        "security": [
          {
            "apiKey": [
              "read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "Its branches.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DatabaseBranchList"
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          },
          "404": {
            "description": "`database_not_found`: the project has no database of this id or name (a deleted one is none).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "database_not_found"
            ]
          }
        }
      },
      "post": {
        "operationId": "createDatabaseBranch",
        "tags": [
          "Databases"
        ],
        "summary": "Make a branch of a database",
        "description": "A branch of its parent (`parent`, a branch's name or id; the root, `main`, unless it names another) as the parent is now (slice DB6, Neon's form): the parent's Drive forked as it is (its node makes a version of it with its guest's writes held a moment: the Drive as at a power cut, which PostgreSQL recovers from by its WAL), nothing copied; and an app of one stateful service on it, as the root's: the same image, size and sleep, an instance of its own that sleeps and wakes on its own, its name on the project's network `<database>-<branch>` (`private_host`), an endpoint of its own. Its roles and passwords are its parent's: they are in the data it was forked of. Its Drive is billed for what it changed since the fork, never more than what it holds (`storage.billed_bytes`, as the store's collector last worked it out: what its newest version reads from objects of its own); what it shares with its parent is the parent's, billed once. With `expires_at` it is deleted then, by itself (once no branch made of it is there). The call waits for its first start (150 s at the most) and answers the branch, or `creating` (it goes on: `GET` it). A branch of a moment of the root's history (`from`): of its Drive's window's version, or of its WAL archive replayed to the moment (seconds to minutes: the call waits). Takes `write`.",
        "security": [
          {
            "apiKey": [
              "write"
            ]
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DatabaseBranchCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The branch: its PostgreSQL answers (`awake`, or asleep since), or `creating`, its first start under way.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DatabaseBranch"
                }
              }
            },
            "headers": {
              "Location": {
                "schema": {
                  "type": "string"
                },
                "description": "`/v1/databases/{id}/branches/{branch}`, the new branch's."
              }
            }
          },
          "400": {
            "description": "`invalid_json`; `unknown_field` (of a later slice too: `from.snapshot`); `missing_field`: no `name`; `invalid_name`: a name that is not one, or that is too long for `<database>-<branch>` (40 characters at the most); `invalid_field`: `parent` is no text, `expires_at` is no moment to come; `unknown_parameter`. `invalid_field` of `from.exact` too. `invalid_history` (`param` `from.at`): a time its root branch's history does not hold (outside `history_seconds`, after now, a whole minute out of reach of both the root's Drive's window and its WAL archive, or a time to the second, or `exact`, out of the archive's reach: before its oldest base, in a gap of it (`history.gaps`), or of a database that keeps none; the message says from when each reaches, and the window's version at or before the time, whose own moment a branch may be made of), or a branch of another than the root (the root alone keeps a history).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "missing_field",
              "invalid_name",
              "invalid_field",
              "unknown_parameter",
              "invalid_history"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "description": "`key_scope`, `needs_api_key`, `account_held` (as everywhere); `product_not_open`: Serverless Database is not open on this platform yet (`--products`); `drive_limit_reached`: one more Drive than the account's plan lets a project keep, as for a fork; `spend_limit_reached`, `balance_used_up`: the project is held back; `account_share_reached`: the account holds its share of the data drive of the node the fork is made on. `plan_cap_reached`: `database_branches`, one branch more than a database of the plan may have (free: 10, paid: 500); on the free plan `database_cu_hours` or `account_database_cu_hours`, the database (or the account's together) computed what it may this calendar month.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "key_scope",
              "needs_api_key",
              "account_held",
              "product_not_open",
              "drive_limit_reached",
              "spend_limit_reached",
              "balance_used_up",
              "account_share_reached",
              "plan_cap_reached"
            ]
          },
          "404": {
            "description": "`database_not_found`; `branch_not_found` (`param` `parent`): the database has no branch of that name or id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "database_not_found",
              "branch_not_found"
            ]
          },
          "409": {
            "description": "`name_taken`: the database has a branch of the name (`main` is the root's), or the project has an app named `<database>-<branch>` or a Drive named `<database>-<branch>-data`; `database_busy` (`retryable`): the database, or the parent, is being made or deleted; `database_failed`: its first start, or the parent's, failed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "name_taken",
              "database_busy",
              "database_failed"
            ]
          },
          "503": {
            "description": "`database_unavailable` (`retryable`): the parent has no Drive now; `node_unavailable`: the parent's node, or the one chosen for the fork, cannot be reached; `drive_in_store`: no node holds the parent's Drive at this moment; `unavailable`: the parent's node could not make its version now; `no_capacity`: no node has room for the branch's instance, or for the machine an archive's replay runs in; `database_archive_unavailable`: the root's machine did not send its last segment of WAL to the archive, or the archive does not reach a time to the second (or `exact`) yet, its store refusing its segments since before it (`database.archive_failing`; the message says since when and the window's version at or before the time); `database_restore_failed`: the archive's replay to the moment did not end in a promoted PostgreSQL (the message has its last words); `drive_unreadable`: the object store does not give every block of the version the branch's Drive is made of (the window's, or the archive's base's snapshot): nothing was made. Ask again in a moment.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "database_unavailable",
              "node_unavailable",
              "drive_in_store",
              "unavailable",
              "no_capacity",
              "database_archive_unavailable",
              "database_restore_failed",
              "drive_unreadable"
            ]
          }
        }
      }
    },
    "/v1/databases/{id}/branches/{branch}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          },
          "description": "The database's id (`db-…`) or its name."
        },
        {
          "name": "branch",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          },
          "description": "The branch's name or id: `main` (the root branch), or a branch of the database's own."
        }
      ],
      "get": {
        "operationId": "getDatabaseBranch",
        "tags": [
          "Databases"
        ],
        "summary": "A branch of a database",
        "description": "Takes `read`.",
        "security": [
          {
            "apiKey": [
              "read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "The branch.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DatabaseBranch"
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          },
          "404": {
            "description": "`database_not_found`; `branch_not_found`: the database has no branch of this name or id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "database_not_found",
              "branch_not_found"
            ]
          }
        }
      },
      "delete": {
        "operationId": "deleteDatabaseBranch",
        "tags": [
          "Databases"
        ],
        "summary": "Delete a branch of a database",
        "description": "Its app is deleted (its instance stops PostgreSQL with a fast shutdown), then its Drive; nothing is kept of it. Not the root branch, which is deleted with the database, nor a branch that a branch that is there was made of. The name is free at once. Takes `write`.",
        "security": [
          {
            "apiKey": [
              "write"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "The branch as it ends: `deleted`; or `deleting` where what is left takes longer, which the control plane finishes.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DatabaseBranch"
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          },
          "404": {
            "description": "`database_not_found`; `branch_not_found`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "database_not_found",
              "branch_not_found"
            ]
          },
          "409": {
            "description": "`root_branch`: `main` is the root branch, deleted with the database (`DELETE /v1/databases/{id}`); `has_branches`: a branch that is there was made of it (the message names them): delete those first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "root_branch",
              "has_branches"
            ]
          }
        }
      }
    },
    "/v1/databases/{id}/branches/{branch}/sleep": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          },
          "description": "The database's id (`db-…`) or its name."
        },
        {
          "name": "branch",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          },
          "description": "The branch's name or id: `main` (the root branch), or a branch of the database's own."
        }
      ],
      "post": {
        "operationId": "sleepDatabaseBranch",
        "tags": [
          "Databases"
        ],
        "summary": "Put a branch of a database to sleep now",
        "description": "Its instance's memory and disk are saved: its sessions are there when it wakes. A connection that is open stays open, and its next message wakes it. Each branch's instance is its own: the others are left as they are. Takes `write`.",
        "security": [
          {
            "apiKey": [
              "write"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "The branch.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DatabaseBranch"
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          },
          "404": {
            "description": "`database_not_found`; `branch_not_found`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "database_not_found",
              "branch_not_found"
            ]
          },
          "409": {
            "description": "`database_busy` (`retryable`): the database, or the branch, is being made or deleted. `database_failed`: its first start failed (`error` says why): delete it and make it again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "database_busy",
              "database_failed"
            ]
          },
          "503": {
            "description": "`database_unavailable` (`retryable`): the database has no instance now, or its PostgreSQL did not take the password; ask again in a moment. `node_unavailable` (`retryable`): the node of its instance cannot be reached.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "database_unavailable",
              "node_unavailable"
            ]
          }
        }
      }
    },
    "/v1/databases/{id}/branches/{branch}/wake": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          },
          "description": "The database's id (`db-…`) or its name."
        },
        {
          "name": "branch",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          },
          "description": "The branch's name or id: `main` (the root branch), or a branch of the database's own."
        }
      ],
      "post": {
        "operationId": "wakeDatabaseBranch",
        "tags": [
          "Databases"
        ],
        "summary": "Wake a branch of a database now",
        "description": "As a connection would wake it, held to the plan's caps on what is awake as any wake is. One that is awake is left as it is. Each branch's instance is its own: the others are left as they are. Takes `write`.",
        "security": [
          {
            "apiKey": [
              "write"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "The branch.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DatabaseBranch"
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "description": "`key_scope`, `needs_api_key`, `account_held` (as everywhere); `plan_cap_reached`: on the free plan the database computed its 100 CU-hours this calendar month (`database_cu_hours`), or the account's databases their 300 together (`account_database_cu_hours`): it sleeps until the next month, or until the account is on a paid plan.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "key_scope",
              "needs_api_key",
              "account_held",
              "plan_cap_reached"
            ]
          },
          "404": {
            "description": "`database_not_found`; `branch_not_found`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "database_not_found",
              "branch_not_found"
            ]
          },
          "409": {
            "description": "`database_busy` (`retryable`): the database, or the branch, is being made or deleted. `database_failed`: its first start failed (`error` says why): delete it and make it again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "database_busy",
              "database_failed"
            ]
          },
          "503": {
            "description": "`database_unavailable` (`retryable`): the database has no instance now, or its PostgreSQL did not take the password; ask again in a moment. `node_unavailable` (`retryable`): the node of its instance cannot be reached.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "database_unavailable",
              "node_unavailable"
            ]
          }
        }
      }
    },
    "/v1/databases/{id}/branches/{branch}/restore": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          },
          "description": "The database's id (`db-…`) or its name."
        },
        {
          "name": "branch",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          },
          "description": "The branch, by its name or its id: the root branch, `main`, alone."
        }
      ],
      "post": {
        "operationId": "restoreDatabaseBranch",
        "tags": [
          "Databases"
        ],
        "summary": "Restore a database's root branch to a time",
        "description": "The root branch restored in place (Neon's form, slices DB8 and DB5) to the moment `at`: to the newest version of its Drive's window at or before it (one every 30 seconds in which it was written; a day at the most), where `at` is a whole minute or that version's own moment; otherwise (further back than the window holds, to the second, or with `exact`) to `at` itself, to the millisecond, by its WAL archive: the newest base before it forked, its WAL replayed to it and promoted to a timeline of its own before the root mounts it (seconds to minutes: the call waits). Its endpoint and its connection strings are what they were, its owner's password too; its PostgreSQL starts again on a fork of its Drive at that version, which it recovers as after a power cut (or which its archive's replay left at the moment), and the connections it had drop. Its state before is kept as a branch of its own, `main_old_<the restore's moment>` (UTC, to the second), which is deleted as any branch: the Drive it had, forked once its server has let go of it, so that the branch holds every write that server answered, those answered while the restore was under way too. The call answers once the database serves on the restored Drive; where it does not within five minutes (its node refused the version of its app on it; that version did not come up and the app's instance was put back; or the instance runs the version on the Drive it had, a minute after the version was ready), or where its state before cannot be kept as that branch, the restore is undone, its Drive of before never let go: the app on the version it had, the database on its Drive, no `main_old_` branch, and the call refused saying why. With an `Idempotency-Key`, the same call again while the first runs waits for it (fifteen minutes at the most) and is given its answer, whatever it is, with `Idempotent-Replayed`: nothing is restored twice; with another body it is refused at once (409 `idempotency_key_reused`). Its 200 is kept for a day, and given to the same call again; after a refusal the same call again is made anew. Takes `write`.",
        "security": [
          {
            "apiKey": [
              "write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DatabaseBranchRestore"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The database, its root branch restored.",
            "headers": {
              "Idempotent-Replayed": {
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                },
                "description": "Present when the answer is the one of an earlier request with the same `Idempotency-Key`: the restore it made, which this request waited for, or one made within the day."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Database"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_json`, `unknown_parameter`; `unknown_field`: a field it does not take; `missing_field`: no `at`; `invalid_field`: `at` is no time, `exact` no boolean; `invalid_history` (`param` `at`): a time its history does not hold: outside `history_seconds`, after now, a whole minute out of reach of both its Drive's window and its WAL archive, or a time to the second (or `exact`) out of the archive's reach (before its oldest base, in a gap of it, `history.gaps`; a database made where no object gateway serves keeps none): the message says from when each reaches, and the window's version at or before the time, whose own moment it may be restored to; `invalid_idempotency_key`: an `Idempotency-Key` that is not 1 to 255 characters.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_parameter",
              "unknown_field",
              "missing_field",
              "invalid_field",
              "invalid_history",
              "invalid_idempotency_key"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "description": "`key_scope`, `needs_api_key`, `account_held` (as everywhere); the node its app's version on the restored Drive is made on refused it (the database is on its Drive as it was, the restored Drive and the branch of before gone): `account_share_reached`, the account would hold more than its share of that node's data drive; `plan_cap_reached`, `spend_limit_reached`, `balance_used_up`, as for any instance.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "key_scope",
              "needs_api_key",
              "account_held",
              "account_share_reached",
              "plan_cap_reached",
              "spend_limit_reached",
              "balance_used_up"
            ]
          },
          "404": {
            "description": "`database_not_found`; `branch_not_found`: the database has no such branch.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "database_not_found",
              "branch_not_found"
            ]
          },
          "409": {
            "description": "`not_root_branch`: the branch is not the root: a restore in place is of the root's alone (make a branch of a moment instead: `POST /v1/databases/{id}/branches` with `from: {at}`); `database_busy`: the database is being made, deleted or changed by another call now; `database_failed`: its first start failed; `idempotency_key_reused`: the `Idempotency-Key` came with another body; `idempotency_in_progress`: the first call with it has run for fifteen minutes and has not answered (retryable).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "not_root_branch",
              "database_busy",
              "database_failed",
              "idempotency_key_reused",
              "idempotency_in_progress"
            ]
          },
          "503": {
            "description": "`database_unavailable`: it has no app or no Drive now, or no instance to send its last segment of WAL: ask again in a moment; `unavailable`, `node_unavailable`: a node its Drive is forked on, or its app is deployed to, cannot be reached now; `database_archive_unavailable`: its machine did not send its last segment to the archive (the message says what it said), or the archive does not reach a time to the second (or `exact`) yet, its store refusing its segments since before it (`database.archive_failing`; the message says since when and the window's version at or before the time): nothing is done; `database_restore_failed`: the archive's replay to the moment did not end in a promoted PostgreSQL (the message has its last words), or the version of its app on the restored Drive did not serve within five minutes (it did not come up, and its instance was put back: `upgrade_failed`; the instance runs it on the Drive it had; or its PostgreSQL did not answer there), the message says why: the restore is undone, the root as it was; `no_capacity`: no node has room for the machine the replay runs in; `drive_unreadable`: the object store does not give every block of the version the restored Drive is made of (the window's, or the archive's base's snapshot): the root is as it was.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "database_unavailable",
              "unavailable",
              "node_unavailable",
              "database_archive_unavailable",
              "database_restore_failed",
              "no_capacity",
              "drive_unreadable"
            ]
          }
        }
      }
    },
    "/v1/databases/{id}/roles": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          },
          "description": "The database's id (`db-…`) or its name."
        }
      ],
      "get": {
        "operationId": "listDatabaseRoles",
        "tags": [
          "Databases"
        ],
        "summary": "A database's roles",
        "description": "The roles its root branch's PostgreSQL lets log in, read from PostgreSQL itself (a role made a moment ago with `CREATE ROLE … LOGIN` is there): its owner (`owner: true`) and the customer's own; the platform's superuser, any other superuser and PostgreSQL's own (`pg_…`) are left out. The root branch is woken for it as a connection would wake it. A branch of its own has its parent's roles as they were when it was made. Any of them is given a new password with `POST /v1/databases/{id}/roles/{role}/reset-password`. An account that owes is refused it (402): its database's machine runs nothing for it. Takes `read`.",
        "security": [
          {
            "apiKey": [
              "read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "Its roles, in the order of their names, on one page.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DatabaseRoleList"
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          },
          "404": {
            "description": "`database_not_found`: the project has no database of this id or name (a deleted one is none).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "database_not_found"
            ]
          },
          "409": {
            "description": "`database_busy` (`retryable`): the database is being made or deleted. `database_failed`: its first start failed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "database_busy",
              "database_failed"
            ]
          },
          "503": {
            "description": "`database_unavailable` (`retryable`): the database has no instance now, or its PostgreSQL did not answer; ask again in a moment. `node_unavailable` (`retryable`): the node of its instance cannot be reached.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "database_unavailable",
              "node_unavailable"
            ]
          }
        }
      }
    },
    "/v1/databases/{id}/roles/{role}/reset-password": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          },
          "description": "The database's id (`db-…`) or its name."
        },
        {
          "name": "role",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          },
          "description": "A role of the database that logs in (`GET /v1/databases/{id}/roles` lists them): its owner (`<database_name>_owner`), or one of the customer's own (`CREATE ROLE … LOGIN`)."
        }
      ],
      "post": {
        "operationId": "resetDatabasePassword",
        "tags": [
          "Databases"
        ],
        "summary": "A new password for a role of a database",
        "description": "Set on its root branch as a SCRAM verifier (the password itself never reaches the machine); the old password is refused from the next login on, and a connection that is open stays. The new one is in this answer and kept nowhere. The owner's answer is the database, with the password and the root branch's connection strings that hold it; another role's (one `GET /v1/databases/{id}/roles` lists) is the role, with its password and the root branch's strings of that role that hold it (`DatabaseRoleSecret`). A branch of its own keeps the password it was made with. Takes `write`.",
        "security": [
          {
            "apiKey": [
              "write"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "The owner's: the database, with the new `password` and the connection strings with it. Another role's: the role, with its new `password` and the strings with it.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/Database"
                    },
                    {
                      "$ref": "#/components/schemas/DatabaseRoleSecret"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          },
          "404": {
            "description": "`database_not_found`; `role_not_found`: the database has no role of the name that logs in and is not the platform's.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "database_not_found",
              "role_not_found"
            ]
          },
          "409": {
            "description": "`database_busy` (`retryable`): the database is being made or deleted. `database_failed`: its first start failed (`error` says why): delete it and make it again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "database_busy",
              "database_failed"
            ]
          },
          "503": {
            "description": "`database_unavailable` (`retryable`): the database has no instance now, or its PostgreSQL did not take the password; ask again in a moment. `node_unavailable` (`retryable`): the node of its instance cannot be reached.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "database_unavailable",
              "node_unavailable"
            ]
          }
        }
      }
    },
    "/v1/volumes": {
      "post": {
        "operationId": "createVolume",
        "tags": [
          "Volumes"
        ],
        "summary": "Make a volume",
        "description": "Makes a volume on a node of the region that keeps volumes, empty but for its file system, and copies it into the object store from then on. A plan's limits hold (403 `drive_limit_reached`); a project that its spend limit holds back, or whose account has used up what it includes and holds nothing, makes none (403). Takes an `Idempotency-Key`: the same key again gives the volume the first call made. Takes `write`.",
        "security": [
          {
            "apiKey": [
              "write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/VolumeCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The volume.",
            "headers": {
              "Location": {
                "schema": {
                  "type": "string"
                },
                "description": "`/v1/volumes/{id}`."
              },
              "Idempotent-Replayed": {
                "schema": {
                  "type": "string"
                },
                "description": "`true` where the answer is the one the same Idempotency-Key was given."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Volume"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_json`, `unknown_field`, `unknown_parameter`; `invalid_field`: `size_bytes` is not a whole number of 4 KiB blocks of at least 16 MiB, or of more than 16 TiB, `history_seconds` is neither 3600 to 86400 nor null or 0, or a field is of the wrong type; `invalid_name`: `name` is no name; `unknown_region`: `region` is none of na, eu, ap; `invalid_idempotency_key`: an Idempotency-Key of more than 255 characters.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter",
              "invalid_field",
              "invalid_name",
              "unknown_region",
              "invalid_idempotency_key"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "description": "`key_scope`, `needs_api_key`, `account_held` (as everywhere); `volume_limit_reached`: one more than the account's plan lets a project keep (3 on Free; 500 on a paid plan, raised on request), larger than one may be (`param` `size_bytes`: 10 GiB on Free, 1 TiB on a paid plan, 16 TiB for an enterprise account by an operator's number), or more than they may hold together (15 GiB on Free; `param` `size_bytes`); `spend_limit_reached`, `balance_used_up`: the project is held back, as for a sandbox. `account_share_reached`: no node of its region has the room for a gigabyte of it (its size, where that is less) under the account's share of the node's data drive.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "key_scope",
              "needs_api_key",
              "account_held",
              "volume_limit_reached",
              "spend_limit_reached",
              "balance_used_up",
              "account_share_reached"
            ]
          },
          "409": {
            "description": "`name_taken`: the project has a volume of the name; `idempotency_key_reused`: the Idempotency-Key came with another body; `idempotency_in_progress`: the first call with it is not answered yet.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "name_taken",
              "idempotency_key_reused",
              "idempotency_in_progress"
            ]
          },
          "503": {
            "description": "`no_capacity`: no node that keeps volumes is there to make it on; `not_served_here`: the nodes that are there keep no volumes; `node_unavailable`, `unavailable`: the node chosen for it could not be reached, or did not answer in time: the same call again (under the same Idempotency-Key) finds what it did.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "no_capacity",
              "not_served_here",
              "node_unavailable",
              "unavailable"
            ]
          }
        }
      },
      "get": {
        "operationId": "listVolumes",
        "tags": [
          "Volumes"
        ],
        "summary": "List the project's volumes",
        "description": "Newest first, a page at a time. Each as its node shows it now; one whose node cannot be reached as it was last seen (`status` `unavailable`); one that no node holds (between nodes) as the control plane holds it. An account that owes sees them (unless the control plane is started with `--debt-allows-delete false`). Takes `read`.",
        "security": [
          {
            "apiKey": [
              "read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "The volumes.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VolumeList"
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`, `invalid_field` (`limit`), `invalid_cursor`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter",
              "invalid_field",
              "invalid_cursor"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          }
        }
      }
    },
    "/v1/volumes/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          },
          "description": "The volume's id or name."
        }
      ],
      "get": {
        "operationId": "getVolume",
        "tags": [
          "Volumes"
        ],
        "summary": "A volume",
        "description": "As its node shows it now; as it was last seen where its node cannot be reached (`status` `unavailable`). Takes `read`.",
        "security": [
          {
            "apiKey": [
              "read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "The volume.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Volume"
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          },
          "404": {
            "description": "`volume_not_found`: the project has no volume of this id or name (a volume of another kind is none).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "volume_not_found"
            ]
          },
          "503": {
            "description": "`node_unavailable`: its node cannot be reached, and the control plane has nothing of it to show.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "node_unavailable"
            ]
          }
        }
      },
      "patch": {
        "operationId": "updateVolume",
        "tags": [
          "Volumes"
        ],
        "summary": "Rename a volume, or set its window of versions",
        "description": "`name`: another name, or none (null), on its node before the answer. `history_seconds`: its window of versions (slice DB8) set, changed, or turned off (null or 0: what it kept goes). Takes `write`.",
        "security": [
          {
            "apiKey": [
              "write"
            ]
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/VolumeUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The volume.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Volume"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_json`, `unknown_field`, `unknown_parameter`; `invalid_field`: `name` is no text, or `history_seconds` is neither 3600 to 86400 nor null or 0; `invalid_name`: it is no name.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter",
              "invalid_field",
              "invalid_name"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          },
          "404": {
            "description": "`volume_not_found`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "volume_not_found"
            ]
          },
          "409": {
            "description": "`name_taken`: another volume of the project's has the name. `managed_by_database`: the volume is a database's (`managed_by`), changed through `/v1/databases` alone.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "name_taken",
              "managed_by_database",
              "managed_by_installation"
            ]
          },
          "503": {
            "description": "`node_unavailable`: its node cannot be reached; `drive_in_store`: no node holds it at this moment (it is going to another): ask again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "node_unavailable",
              "drive_in_store"
            ]
          }
        }
      },
      "delete": {
        "operationId": "deleteVolume",
        "tags": [
          "Volumes"
        ],
        "summary": "Delete a volume",
        "description": "With what it holds: on its node before the answer, and its copy in the object store goes with the collector's next round past its grace. An account that owes may (unless the control plane is started with `--debt-allows-delete false`). Takes `write`.",
        "security": [
          {
            "apiKey": [
              "write"
            ]
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted."
          },
          "400": {
            "description": "`unknown_parameter`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          },
          "404": {
            "description": "`volume_not_found`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "volume_not_found"
            ]
          },
          "409": {
            "description": "`volume_in_use`: a sandbox has it: detach it first (`DELETE /v1/sandboxes/{id}/volumes/{volume}`); `key_drive`: it is a keyed instance's, and goes with its key. `managed_by_database`: the volume is a database's (`managed_by`), changed through `/v1/databases` alone.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "volume_in_use",
              "key_drive",
              "managed_by_database",
              "managed_by_installation"
            ]
          },
          "503": {
            "description": "`node_unavailable`: its node cannot be reached.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "node_unavailable"
            ]
          }
        }
      }
    },
    "/v1/volumes/{id}/snapshots": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          },
          "description": "The volume's id or name; a volume deleted since, by its id (its snapshots outlive it)."
        }
      ],
      "post": {
        "operationId": "createVolumeSnapshot",
        "tags": [
          "Volumes"
        ],
        "summary": "Take a snapshot of a volume",
        "description": "Keeps the volume as it is now (slice D7): a version of it in the object store, which its node makes; nothing of it is copied, there or here. A volume a running sandbox has read-write is snapshotted in two steps: a version of what was written up to the call, made while its guest writes on, then one of what it wrote meanwhile, with its writes held, its file system frozen by the sandbox's agent where `fs_consistent` is true (the agent writes out what the file system holds first, and the snapshot mounts without replaying a journal: `consistency` `frozen`), its machine paused otherwise (the volume as at a power cut, which its file system's journal is for: `paused`). The hold lasts as long as reading the last of it takes, ten seconds at the most, after which the snapshot is refused (503 `unavailable`, `retryable`) and the guest goes on. A volume no running sandbox has is snapshotted as it is (`at_rest`). A snapshot outlives its volume: it is listed, read, forked and mounted read-only after the volume is deleted. It is billed for what it keeps alone (`exclusive_bytes`), by the hour, at the volumes' price, on the meter of storage under its own id (`GET /v1/usage?snapshot_id=`). A project that a spend limit holds back, or whose account has used up what it includes and holds nothing, takes none (403). Takes `write`.",
        "security": [
          {
            "apiKey": [
              "write"
            ]
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/VolumeSnapshotCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The snapshot.",
            "headers": {
              "Location": {
                "schema": {
                  "type": "string"
                },
                "description": "`/v1/volumes/{id}/snapshots/{snapshot}`."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VolumeSnapshot"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_json`, `unknown_field`, `unknown_parameter`; `invalid_field`: `name` is no text, or `fs_consistent` is not true or false; `invalid_name`: `name` is no name.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter",
              "invalid_field",
              "invalid_name"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "description": "`key_scope`, `needs_api_key`, `account_held` (as everywhere); `spend_limit_reached`, `balance_used_up`: the project is held back, as for a sandbox.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "key_scope",
              "needs_api_key",
              "account_held",
              "spend_limit_reached",
              "balance_used_up"
            ]
          },
          "404": {
            "description": "`volume_not_found`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "volume_not_found"
            ]
          },
          "409": {
            "description": "`not_frozen`: `fs_consistent` was asked, and the sandbox's agent could not freeze the volume's file system (the agent of a template built before backups does not): take it without; `not_copied`: the volume's node copies nothing to an object store, where a snapshot is a version of it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "not_frozen",
              "not_copied"
            ]
          },
          "502": {
            "description": "`node_refused`: the volume's node refused in words the control plane has none of its own for (the message has the node's).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "node_refused"
            ]
          },
          "503": {
            "description": "`node_unavailable`: the volume's node cannot be reached; `drive_in_store`: no node holds it at this moment (it is going to another): ask again; `unavailable`: its node could not make the version now (what its guest wrote was not read within the ten seconds of the hold, the object store does not hold the volume as it is yet, or it is being let go to another node): ask again in a moment.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "node_unavailable",
              "drive_in_store",
              "unavailable"
            ]
          }
        }
      },
      "get": {
        "operationId": "listVolumeSnapshots",
        "tags": [
          "Volumes"
        ],
        "summary": "A volume's snapshots",
        "description": "Its snapshots that are not deleted, the newest first, on one page. Of a volume deleted since, by its id. A volume the project never had has none. Takes `read`.",
        "security": [
          {
            "apiKey": [
              "read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "The snapshots.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VolumeSnapshotList"
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          }
        }
      }
    },
    "/v1/volumes/{id}/snapshots/{snapshot}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          },
          "description": "The volume's id or name; a volume deleted since, by its id."
        },
        {
          "name": "snapshot",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          },
          "description": "The snapshot's id (`dsnap-…`)."
        }
      ],
      "get": {
        "operationId": "getVolumeSnapshot",
        "tags": [
          "Volumes"
        ],
        "summary": "A snapshot of a volume",
        "description": "With what it keeps alone as the store's collector last worked it out (`exclusive_bytes`, `exclusive_at`). Takes `read`.",
        "security": [
          {
            "apiKey": [
              "read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "The snapshot.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VolumeSnapshot"
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          },
          "404": {
            "description": "`snapshot_not_found`: the volume has no snapshot of this id, or it is deleted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "snapshot_not_found"
            ]
          }
        }
      },
      "delete": {
        "operationId": "deleteVolumeSnapshot",
        "tags": [
          "Volumes"
        ],
        "summary": "Delete a snapshot of a volume",
        "description": "Gone from the books at once, and billed up to then; the store's collector deletes what it alone kept once its delete is older than the collector's grace (a day). The forks made of it keep what they need, and a sandbox that has it mounted read-only keeps the mount until it is taken away (`DELETE /v1/sandboxes/{id}/volumes/{snapshot}`) or the sandbox is deleted. An account that owes may (unless the control plane is started with `--debt-allows-delete false`). Takes `write`.",
        "security": [
          {
            "apiKey": [
              "write"
            ]
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted."
          },
          "400": {
            "description": "`unknown_parameter`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          },
          "404": {
            "description": "`snapshot_not_found`: the volume has no snapshot of this id, or it is deleted already.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "snapshot_not_found"
            ]
          }
        }
      }
    },
    "/v1/volume-snapshots": {
      "get": {
        "operationId": "listProjectVolumeSnapshots",
        "tags": [
          "Volumes"
        ],
        "summary": "The project's snapshots of volumes",
        "description": "Every snapshot of a volume of the project's that is not deleted, of every volume, those of volumes deleted since among them, the newest first, on one page. A snapshot outlives its volume: this is where one of a volume deleted since is found (`volume_id` is that volume's), and it is mounted read-only (`POST /v1/sandboxes/{id}/volumes` with `mode` `snapshot`), forked (`POST /v1/volumes/{id}/fork` with `from_snapshot`) and deleted (`DELETE /v1/volumes/{id}/snapshots/{snapshot}`) by that id as any other is. `volume_deleted` `true` lists only those whose volume is deleted since, `false` only those whose volume is there. Takes `read`.",
        "security": [
          {
            "apiKey": [
              "read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "volume_deleted",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "true",
                "false"
              ]
            },
            "description": "`true`: only the snapshots whose volume is deleted since; `false`: only those whose volume is there. Not given: both."
          }
        ],
        "responses": {
          "200": {
            "description": "The snapshots.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VolumeSnapshotList"
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`; `invalid_field` (`param` `volume_deleted`): `volume_deleted` that is neither `true` nor `false`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter",
              "invalid_field"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          }
        }
      }
    },
    "/v1/volumes/{id}/fork": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          },
          "description": "The volume's id or name; with `from_snapshot`, a volume deleted since, by its id."
        }
      ],
      "post": {
        "operationId": "forkVolume",
        "tags": [
          "Volumes"
        ],
        "summary": "Fork a volume",
        "description": "A new volume whose first version is the snapshot's (`from_snapshot`), that of the newest version of the volume's window of versions at or before a time (`at`; `history_seconds`, slice DB8) or, where neither is given, the volume's as it is now (a version made for the fork as a snapshot's is, and not kept). Nothing is copied: it is mounted at once, on any node of the volume's object store, and its blocks come from the store as its guest reads them; it is made on a node of the region its version is read in, the volume's (a snapshot keeps the region its volume was in, and its size, for when the volume is deleted). It is the volume's size, the project's, and says what it was made of (`parent_volume_id`, the volume; `root_volume_id`, the first volume of the line). A snapshot of a volume deleted since is forked by the volume's id. It is billed as any volume is, by what it holds (`used_bytes`), what the snapshot holds among it: a fork of a 10 GB snapshot is billed as a volume of 10 GB. Held to the plan's volumes as a create is (403 `drive_limit_reached`), and held back as a create is by a spend limit, or an account that has used up what it includes and holds nothing. Takes `write`.",
        "security": [
          {
            "apiKey": [
              "write"
            ]
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/VolumeFork"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The new volume.",
            "headers": {
              "Location": {
                "schema": {
                  "type": "string"
                },
                "description": "`/v1/volumes/{id}`, the new volume's."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Volume"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_json`, `unknown_field`, `unknown_parameter`; `invalid_field`: `name` or `from_snapshot` is no text, `at` no time, or `from_snapshot` and `at` are both given; `invalid_name`: `name` is no name; `invalid_history` (`param` `at`): the volume keeps no window of versions, or `at` is before it, before its first version, or after now (the message says the window).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter",
              "invalid_field",
              "invalid_name",
              "invalid_history"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "description": "`key_scope`, `needs_api_key`, `account_held` (as everywhere); `volume_limit_reached`: one more volume than the account's plan lets a project keep, or more than they may hold together, as for a create (`param` `size_bytes` of the second); `spend_limit_reached`, `balance_used_up`: the project is held back, as for a sandbox; `account_share_reached`: the account holds its share of the data drive of the node the fork is made on (a fork goes to a node where it has room for a gigabyte to write, where one has it), in the node's words.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "key_scope",
              "needs_api_key",
              "account_held",
              "volume_limit_reached",
              "spend_limit_reached",
              "balance_used_up",
              "account_share_reached"
            ]
          },
          "404": {
            "description": "`volume_not_found`: no volume of this id or name (a volume deleted since is forked from one of its snapshots alone); `snapshot_not_found` (`param` `from_snapshot`): the volume has no snapshot of that id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "volume_not_found",
              "snapshot_not_found"
            ]
          },
          "409": {
            "description": "`name_taken`: the project has a volume of the name; `not_copied`: the volume's node copies nothing to an object store, where a fork is made of a version of it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "name_taken",
              "not_copied"
            ]
          },
          "502": {
            "description": "`node_refused`: a node refused in words the control plane has none of its own for (the message has the node's).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "node_refused"
            ]
          },
          "503": {
            "description": "`node_unavailable`: the volume's node, or the one chosen for the fork, cannot be reached, or does not hold the project's keys yet; `drive_in_store`: no node holds the volume at this moment (it is going to another); `unavailable`: no node that keeps volumes and copies to the object store can be called now, the volume's node could not make its version now, or the fork is made and not listed yet; `drive_unreadable`: the object store does not give every object of the version (one is not there): nothing was made. Ask again in a moment.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "node_unavailable",
              "drive_in_store",
              "unavailable",
              "drive_unreadable"
            ]
          }
        }
      }
    },
    "/v1/volumes/{id}/versions": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          },
          "description": "The volume's id or name."
        }
      ],
      "get": {
        "operationId": "listVolumeVersions",
        "tags": [
          "Volumes"
        ],
        "summary": "List the versions a volume's window keeps",
        "description": "The versions of the volume its window of versions keeps (`history_seconds`, slice DB8), the newest first: every window (30 seconds) in which the volume was written has one, the volume as it was at one moment (`at`). A restore (`POST /v1/volumes/{id}/restore` with `at`) or a fork (`POST /v1/volumes/{id}/fork` with `at`) to a time takes the newest at or before it; with `at`, the one it takes. A volume with no window has none. Takes `read`.",
        "security": [
          {
            "apiKey": [
              "read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "at",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "A time within the window: the version a restore to it takes, alone."
          }
        ],
        "responses": {
          "200": {
            "description": "The versions.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VolumeVersionList"
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`; `invalid_field`: `at` is no time; `invalid_history` (`param` `at`): the volume keeps no window of versions, or `at` is before it, before its first version, or after now (the message says the window).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter",
              "invalid_field",
              "invalid_history"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          },
          "404": {
            "description": "`volume_not_found`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "volume_not_found"
            ]
          }
        }
      }
    },
    "/v1/volumes/{id}/restore": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          },
          "description": "The volume's id or name."
        }
      ],
      "post": {
        "operationId": "restoreVolume",
        "tags": [
          "Volumes"
        ],
        "summary": "Restore a volume to one of its snapshots, or to a time",
        "description": "The volume's newest version is made the snapshot's (`snapshot`), or that of the newest version of its window of versions at or before a time (`at`; `history_seconds`, slice DB8): a version after its head whose content is that one's; nothing is copied, and the versions in between stay where a snapshot or a fork keeps them. Refused while a sandbox has it read-write (409 `volume_in_use`: detach it first). Its node lets it go and takes it again from that version, one epoch above; what it held here is fetched again as its guest reads it. Takes `write`.",
        "security": [
          {
            "apiKey": [
              "write"
            ]
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/VolumeRestore"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The volume, as the snapshot had it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Volume"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_json`, `unknown_field`, `unknown_parameter`; `missing_field`: neither `snapshot` nor `at`; `invalid_field`: `snapshot` is no text, `at` no time, or both are given; `invalid_history` (`param` `at`): the volume keeps no window of versions, or `at` is before it, before its first version, or after now (the message says the window).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter",
              "missing_field",
              "invalid_field",
              "invalid_history"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "description": "`key_scope`, `needs_api_key`, `account_held` (as everywhere); `account_share_reached`: the account has not the room for the volume taken again (a gigabyte to write) under its share of its node's data drive, in the node's words: it stays as it was.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "key_scope",
              "needs_api_key",
              "account_held",
              "account_share_reached"
            ]
          },
          "404": {
            "description": "`volume_not_found`; `snapshot_not_found` (`param` `snapshot`): the volume has no snapshot of that id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "volume_not_found",
              "snapshot_not_found"
            ]
          },
          "409": {
            "description": "`volume_in_use`: a sandbox has it read-write: detach it first (`DELETE /v1/sandboxes/{id}/volumes/{volume}`); `not_copied`: its node copies nothing to an object store. `managed_by_database`: the volume is a database's (`managed_by`), changed through `/v1/databases` alone.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "volume_in_use",
              "not_copied",
              "managed_by_database",
              "managed_by_installation"
            ]
          },
          "502": {
            "description": "`node_refused`: its node refused in words the control plane has none of its own for (the message has the node's).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "node_refused"
            ]
          },
          "503": {
            "description": "`node_unavailable`: its node cannot be reached; `drive_in_store`: no node holds it at this moment; `unavailable`: its node could not let it go or take it again now (the object store does not hold it as it is yet); `drive_unreadable`: the object store does not give every object of the version (one is not there): the volume is as it was. Ask again in a moment.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "node_unavailable",
              "drive_in_store",
              "unavailable",
              "drive_unreadable"
            ]
          }
        }
      }
    },
    "/v1/volumes/{id}/home": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          },
          "description": "The volume's id or name."
        }
      ],
      "post": {
        "operationId": "moveVolumeHome",
        "tags": [
          "Volumes"
        ],
        "summary": "Move a volume's home to the region it is in",
        "description": "A volume's home is the region whose object store holds its copy (`home`): the region it was made in, until it moves. A volume mounted by a sandbox in another region (a sandbox made in that region with it, or given it there) is read from its home's store as its guest reads it, and what its guest writes goes to the store of the region it is in; its home moves there by itself once it has been there for an hour, or at once by this call: its node copies what is still in its home's store to its own, and `home` is the new region once nothing is left there (`home_move` says how far it is). Its old home's copy goes once nothing needs it any more (after a day). A volume's home moves once in six hours at the most, by itself or by this call (409 `home_moved_recently`): a volume used in two regions by turns is not copied back and forth. Takes `write`.",
        "security": [
          {
            "apiKey": [
              "write"
            ]
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "properties": {}
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "The move is under way: the volume, with `home_move`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Volume"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_json`, `unknown_field`, `unknown_parameter`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "description": "`key_scope`, `needs_api_key`, `account_held` (as everywhere).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "key_scope",
              "needs_api_key",
              "account_held"
            ]
          },
          "404": {
            "description": "`volume_not_found`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "volume_not_found"
            ]
          },
          "409": {
            "description": "Its home does not move. `home_here`: its home is the region it is held in; `home_moved_recently`: its home moved within the last six hours (`retry_after_seconds` says when it may move again); `managed_by_database`: the volume is a database's (`managed_by`), changed through `/v1/databases` alone.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "home_here",
              "home_moved_recently",
              "managed_by_database",
              "managed_by_installation"
            ]
          },
          "503": {
            "description": "`node_unavailable`: its node cannot be reached; `drive_in_store`: no node holds it at this moment (its home moves to the region a sandbox that mounts it is in); `unavailable`: its node did not begin the move: ask again in a moment.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "node_unavailable",
              "drive_in_store",
              "unavailable"
            ]
          }
        }
      }
    },
    "/v1/sandboxes/{id}/volumes": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          },
          "description": "The sandbox's id or name."
        }
      ],
      "post": {
        "operationId": "attachVolume",
        "tags": [
          "Volumes"
        ],
        "summary": "Attach a volume to a running sandbox",
        "description": "The sandbox mounts the volume at `path`, read-write, at once (it is woken if it sleeps). A volume another node holds is brought to the sandbox's node first: its node lets it go once the object store holds it as it is, and the sandbox's lays it out block by block, reading the old node first. With `mode` `snapshot` and `snapshot` (slice D7): the sandbox mounts that snapshot of the volume read-only, on its own node, whatever has the volume, the volume deleted since too, where its node is of the region the snapshot is read in, the one its volume was in when it was taken, which the snapshot keeps (409 `not_on_one_machine` otherwise: D9 mounts elsewhere); its blocks come from the store as its guest reads them, billed to the sandbox's account as a volume's cold reads, and the mount is answered with the snapshot. A sandbox has 8 volumes and volumes of its own at the most. Takes `write`.",
        "security": [
          {
            "apiKey": [
              "write"
            ]
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/VolumeMount"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The volume, attached; a snapshot mounted read-only (`mode` `snapshot`), the snapshot.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/Volume"
                    },
                    {
                      "$ref": "#/components/schemas/VolumeSnapshot"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "`invalid_json`, `unknown_field`, `unknown_parameter`; `missing_field`: no `drive` or no `path`, or (`param` `snapshot`) `mode` `snapshot` without the snapshot; `invalid_field`: `path` is not an absolute path other than `/`, `mode` is neither `read_write` nor `snapshot`, or `snapshot` is given without `mode` `snapshot`, or names a snapshot of another volume; `invalid_path`: the node mounts nothing at `path` (a path of the system's own).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "invalid_json",
              "unknown_field",
              "unknown_parameter",
              "missing_field",
              "invalid_field",
              "invalid_path"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "description": "`key_scope`, `needs_api_key`, `account_held` (as everywhere); `account_share_reached`: a snapshot mounted read-only (`mode` `snapshot`) on a node where the account holds its share of the data drive already, in the node's words (what the guest reads of the snapshot is held there, and counts).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "key_scope",
              "needs_api_key",
              "account_held",
              "account_share_reached"
            ]
          },
          "404": {
            "description": "`volume_not_found`, `sandbox_not_found`; `snapshot_not_found` (`param` `snapshot`): the project has no snapshot of a volume of that id that is not deleted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "volume_not_found",
              "sandbox_not_found",
              "snapshot_not_found"
            ]
          },
          "409": {
            "description": "`volume_in_use`: another sandbox has it read-write (a volume is one sandbox's at a time; a read-only mount of one of its snapshots is any number's), or this one has it already; `too_many_drives`: the sandbox has 8; `too_many_volumes`: its machine has fewer spare drives than that (a sandbox made from a template or a snapshot of before there were eight, until it starts from its disk); `key_drive`: it is a keyed instance's; `mount_path_taken`: something else is mounted at `path`; `detached_mount_in_use`: every spare drive of the sandbox's machine with no volume behind it is still held by a file system a process in it has open, of a volume detached from it with `?force=true` while the process used it (it takes one again once the process lets go of it, or the sandbox restarts); `not_on_one_machine`: its node is in another region, or copies to another object store, than the sandbox's; of a snapshot (`mode` `snapshot`, `param` `snapshot`), the sandbox's node is in another region than the one the snapshot is read in (its volume's when it was taken, deleted since or not); `not_copied`, `store_unfenced`: the sandbox's node copies nothing to an object store, or to one that no move goes over, and the volume is on another; `volume_restoring`: it is being made from a backup still; `sandbox_failed`: the sandbox failed. `managed_by_database`: the volume is a database's (`managed_by`), changed through `/v1/databases` alone.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "volume_in_use",
              "too_many_drives",
              "too_many_volumes",
              "key_drive",
              "mount_path_taken",
              "detached_mount_in_use",
              "not_on_one_machine",
              "not_copied",
              "store_unfenced",
              "volume_restoring",
              "sandbox_failed",
              "managed_by_database",
              "managed_by_installation"
            ]
          },
          "503": {
            "description": "`node_unavailable`: the sandbox's node, or the volume's, cannot be reached (a volume whose node is silent is brought once that node's lease has run out); `copy_behind`: the object store does not hold the volume as it is yet: ask again in a moment; `drive_not_brought`: its node did not let it go, or the sandbox's node could not take it (the message says why); `not_served_here`: the sandbox's node keeps no volumes; `unavailable`: the volume is being brought to another node by another call: ask again in a moment.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "node_unavailable",
              "copy_behind",
              "drive_not_brought",
              "not_served_here",
              "unavailable"
            ]
          }
        }
      }
    },
    "/v1/sandboxes/{id}/volumes/{volume}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          },
          "description": "The sandbox's id or name."
        },
        {
          "name": "volume",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          },
          "description": "The volume's id or name; or the id of a snapshot of a volume (`dsnap-…`) the sandbox has mounted read-only."
        }
      ],
      "delete": {
        "operationId": "detachVolume",
        "tags": [
          "Volumes"
        ],
        "summary": "Detach a volume from a sandbox",
        "description": "The sandbox unmounts the volume (its file system written out first) and has it no more; the volume stays where it is, with what it holds, for any sandbox of its region. A volume's id or name takes away its read-write mount alone; a snapshot's id (`dsnap-…`, slice D7) takes away the sandbox's read-only mount of that snapshot, and the node's view of it with it, the volume and the snapshot deleted since or not, answered with the snapshot. `?force=true`: a mount that a process still uses is taken away all the same. Takes `write`.",
        "security": [
          {
            "apiKey": [
              "write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "force",
            "in": "query",
            "schema": {
              "type": "boolean"
            },
            "description": "Take the mount away while a process uses it."
          }
        ],
        "responses": {
          "200": {
            "description": "The volume, detached; a snapshot's read-only mount taken away, the snapshot.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/Volume"
                    },
                    {
                      "$ref": "#/components/schemas/VolumeSnapshot"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "`unknown_parameter`; `invalid_field`: `force` is not true or false.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "unknown_parameter",
              "invalid_field"
            ]
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/AccountInDebt"
          },
          "403": {
            "$ref": "#/components/responses/NotAllowed"
          },
          "404": {
            "description": "`volume_not_found`, `sandbox_not_found`; `snapshot_not_found`: the project has no snapshot of a volume of that id; `drive_not_attached`: the sandbox does not have it read-write (the message says where it has one of its snapshots read-only, which is taken away by the snapshot's id), or has no read-only mount of the snapshot.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "volume_not_found",
              "sandbox_not_found",
              "snapshot_not_found",
              "drive_not_attached"
            ]
          },
          "409": {
            "description": "`mount_in_use`: a process in the sandbox uses it (`?force=true` takes it away all the same); `key_drive`: it is a keyed instance's; `sandbox_failed`: the sandbox failed. `managed_by_database`: the volume is a database's (`managed_by`), changed through `/v1/databases` alone.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "mount_in_use",
              "key_drive",
              "sandbox_failed",
              "managed_by_database",
              "managed_by_installation"
            ]
          },
          "503": {
            "description": "`node_unavailable`: the sandbox's node cannot be reached; `unavailable`: it did not answer in time.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "x-codes": [
              "node_unavailable",
              "unavailable"
            ]
          }
        }
      }
    }
  },
  "webhooks": {
    "event": {
      "post": {
        "operationId": "deliverEvent",
        "tags": [
          "Webhooks"
        ],
        "summary": "An event, as it is posted to a webhook",
        "description": "Signed as Standard Webhooks signs (https://www.standardwebhooks.com): `webhook-signature` is `v1,` and the base64 of HMAC-SHA256 over `<webhook-id>.<webhook-timestamp>.<the body>`, keyed with the bytes the webhook's secret is the base64 of (what follows `whsec_`); libraries that check it exist for most languages. A delivery has succeeded when the answer's status is 2xx; another status, no connection, and no answer within 10 seconds are failures, and it is sent again 5 seconds, 5 minutes, 30 minutes, 2 hours, 5 hours, 10 hours and 10 hours later (`--webhook-retry-seconds`), then given up. What is to be sent is in the store: a control plane that starts again goes on with it. The same event may come twice (an answer lost on its way back is a failure here): the receiver tells by `webhook-id`. Sent to the address that was checked to be on the public internet, connected to by that address.",
        "parameters": [
          {
            "name": "webhook-id",
            "in": "header",
            "required": true,
            "description": "The event's id: the same each time it is sent.",
            "schema": {
              "type": "string",
              "pattern": "^evt-[0-9a-hjkmnp-tv-z]{16}$"
            }
          },
          {
            "name": "webhook-timestamp",
            "in": "header",
            "required": true,
            "description": "When it was sent, in seconds since 1970.",
            "schema": {
              "type": "string",
              "pattern": "^[0-9]+$"
            }
          },
          {
            "name": "webhook-signature",
            "in": "header",
            "required": true,
            "description": "`v1,<base64>`.",
            "schema": {
              "type": "string",
              "pattern": "^v1,[A-Za-z0-9+/]{43}=$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Event"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx: delivered."
          },
          "500": {
            "description": "Anything else: sent again later, as said above."
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "SignUp": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "password"
        ],
        "properties": {
          "email": {
            "type": "string",
            "description": "The address: needed but with `invitation`, where it is the invitation's if it is given."
          },
          "password": {
            "type": "string",
            "minLength": 10,
            "maxLength": 256,
            "description": "10 to 256 characters, not the address."
          },
          "account_name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100,
            "description": "What the user's own account is called; left out, the address. Not with `invitation`."
          },
          "invitation": {
            "type": "string",
            "description": "The token of an invitation's link (`in_...`), to sign up through."
          }
        }
      },
      "User": {
        "type": "object",
        "additionalProperties": false,
        "description": "A user of the console: an address and a password (kept as its Argon2id hash, and in no answer).",
        "required": [
          "object",
          "id",
          "email",
          "verified",
          "created_at",
          "password_changed_at"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "user"
          },
          "id": {
            "type": "string",
            "pattern": "^usr-[0-9a-hjkmnp-tv-z]{16}$"
          },
          "email": {
            "type": "string",
            "description": "The address, in lower case."
          },
          "verified": {
            "type": "boolean",
            "description": "Whether the address is verified: by the link of its mail (`POST /v1/auth/verify`), by an invitation that came to it, or by a new password chosen through a mail."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "password_changed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the password was last chosen; null: the user has none (GitHub or Google made it, and it has chosen none since)."
          }
        }
      },
      "SignUpAccepted": {
        "type": "object",
        "additionalProperties": false,
        "description": "A sign-up taken: the address is mailed.",
        "required": [
          "object",
          "email"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "sign_up"
          },
          "email": {
            "type": "string",
            "description": "The address, as it is kept."
          }
        }
      },
      "ErrorResponse": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "error"
        ],
        "properties": {
          "move_id": {
            "type": "string",
            "description": "With `already_moving`: the move of the sandbox that is under way."
          },
          "upgrade_id": {
            "type": "string",
            "pattern": "^upg-[0-9a-hjkmnp-tv-z]{16}$",
            "description": "With `upgrade_in_progress` (the key's upgrade that is not over), `upgrade_started` (the upgrade that has begun) and `rollback_gone` (the upgrade whose revert is refused): that upgrade."
          },
          "move": {
            "$ref": "#/components/schemas/Move",
            "description": "With `move_over`, `move_going_back` and `move_taken_over` (`POST /internal/moves/{id}/cancel`): the move, as it stands."
          },
          "lease_until": {
            "type": "string",
            "format": "date-time",
            "description": "With `lease_held`: until when the node may still hold the lease it was granted last, and the time after it for the node to have seen it run out (`recoverNode` is refused until then)."
          },
          "active": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": false,
            "description": "With `standby`: the active copy, as the standby last read the lease; null while no copy holds it.",
            "required": [
              "name",
              "url",
              "term"
            ],
            "properties": {
              "name": {
                "type": "string"
              },
              "url": {
                "type": "string"
              },
              "term": {
                "type": "integer",
                "minimum": 1
              }
            }
          },
          "allowed_hosts": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "string"
            },
            "description": "With `secret_host_not_allowed`: the hosts the secret may go to, as the secret has them; null where it was given none (the node's `SecretHosts`)."
          },
          "problems": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "at",
                "message"
              ],
              "properties": {
                "at": {
                  "type": "string",
                  "description": "Where in the definition, a dotted path (`services.web.port`); empty for the whole of it."
                },
                "message": {
                  "type": "string"
                }
              }
            },
            "description": "With `invalid_definition`: every problem of the definition, each where it is and what it is."
          },
          "report": {
            "$ref": "#/components/schemas/ComposeReport",
            "description": "With `compose_refused`: the import's report, which says what is refused, why, and what to change."
          },
          "accounts": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "id",
                "name"
              ],
              "properties": {
                "id": {
                  "type": "string",
                  "pattern": "^acct-[0-9a-hjkmnp-tv-z]{16}$"
                },
                "name": {
                  "type": "string"
                }
              }
            },
            "description": "With `sole_owner`: the accounts the user is the only owner of."
          },
          "error": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "type",
              "code",
              "message",
              "param",
              "retryable",
              "request_id",
              "docs_url"
            ],
            "properties": {
              "type": {
                "type": "string"
              },
              "code": {
                "type": "string"
              },
              "message": {
                "type": "string"
              },
              "param": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "retryable": {
                "type": "boolean"
              },
              "request_id": {
                "type": "string"
              },
              "docs_url": {
                "type": [
                  "string",
                  "null"
                ]
              }
            }
          }
        }
      },
      "Move": {
        "type": "object",
        "additionalProperties": false,
        "description": "A move of a sandbox from one node to another, with its processes (control/README.md, 沙箱换节点), as the store keeps it: under way, or over.",
        "required": [
          "object",
          "id",
          "sandbox_id",
          "project_id",
          "from",
          "to",
          "epoch",
          "step",
          "why",
          "wake",
          "from_store",
          "release",
          "takeover",
          "error",
          "attempts",
          "next_attempt_at",
          "batch",
          "created_at",
          "updated_at",
          "ended_at"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "move"
          },
          "id": {
            "type": "string",
            "pattern": "^mov-[0-9a-hjkmnp-tv-z]{16}$"
          },
          "sandbox_id": {
            "type": "string"
          },
          "project_id": {
            "type": "string"
          },
          "from": {
            "$ref": "#/components/schemas/MoveNode"
          },
          "to": {
            "$ref": "#/components/schemas/MoveNode"
          },
          "epoch": {
            "type": "integer",
            "minimum": 1,
            "description": "The epoch the table held the sandbox at when the move began: the old node releases it at it, the new one takes it over one on, the old one takes it back two on, and from the object store three on."
          },
          "step": {
            "type": "string",
            "x-extensible-enum": [
              "releasing",
              "taking_over",
              "reclaiming",
              "restoring",
              "done",
              "failed"
            ],
            "description": "Where it stands: `releasing` (the old node is asked to release it; the table has it there), `taking_over` (the new node is asked to take it over; the table has it there), `reclaiming` (the new node refused it, and the old one is asked to take it back), `restoring` (the old one kept nothing of it, and takes it over from the object store), `done` (on the new node), `failed` (where it was, or back there, or where a failure no time clears or an operator's cancel found it: `error` says why)."
          },
          "why": {
            "type": "string",
            "x-extensible-enum": [
              "operator",
              "evacuate",
              "recover"
            ],
            "description": "What made it: the operator's move, an evacuation, a recovery."
          },
          "wake": {
            "type": "boolean",
            "description": "Whether the new node wakes it: as the call said, or as it was (it ran) when the move began."
          },
          "from_store": {
            "type": "boolean",
            "description": "The new node takes it from the object store alone."
          },
          "release": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": true,
            "description": "What the old node's release answered (sandboxd/openapi/openapi.json, `releaseSandbox`), without the release's key; null before it answered."
          },
          "takeover": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": true,
            "description": "What the takeover that ended it answered (`takeoverSandbox` of the node's document): where its disk and memory came from (`source`), how long each took (`ms`), `memory`; null before."
          },
          "error": {
            "oneOf": [
              {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "code",
                  "message",
                  "status"
                ],
                "properties": {
                  "code": {
                    "type": "string",
                    "description": "The node's code, `no_answer` (the call got no answer), `node_unavailable` (the node could not be reached), `recovered`, `node_back` (a recovery's takeover that had not happened, given up: the node the sandbox was taken from came back with it, and serves it again), `cancelled` (an operator ended it: `POST /internal/moves/{id}/cancel`), or what no time clears, which ends it where it stands: `account_closed` (the sandbox's account is closed and its keys destroyed: nothing of it is placed anywhere, and the closure deletes it where it is), `key_destroyed` (its disk, as the old node's last inventory named its keys, is sealed with a version that is destroyed: it opens on no node), `node_not_found` (a node of the move is off the books)."
                  },
                  "message": {
                    "type": "string"
                  },
                  "status": {
                    "type": "integer",
                    "description": "The node's status; 0 where none came."
                  },
                  "takeover_refused": {
                    "type": "object",
                    "additionalProperties": false,
                    "required": [
                      "code",
                      "message",
                      "status"
                    ],
                    "properties": {
                      "code": {
                        "type": "string"
                      },
                      "message": {
                        "type": "string"
                      },
                      "status": {
                        "type": "integer"
                      }
                    },
                    "description": "Of one under way in a step that takes it back to the old node (`reclaiming`, `restoring`): the new node's refusal of the takeover, which is why it fails."
                  },
                  "image": {
                    "type": "string",
                    "description": "With `image_missing` (a takeover found no image the sandbox's disk is over in the object store, and the node no template over it): the image's id, as the node named it."
                  },
                  "holder": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "description": "With `image_missing`: the node that has a template over the image (as its report said it: `disk_image`) and was asked about it last: the node the sandbox goes to, where it has one by now, or one that was asked to send the image (`POST /internal/images/{image}/send` of the node); null where no node that is there and copies to the same object store has one."
                  },
                  "holder_says": {
                    "oneOf": [
                      {
                        "type": "string",
                        "enum": [
                          "has",
                          "sending",
                          "stored"
                        ]
                      },
                      {
                        "type": "null"
                      }
                    ],
                    "description": "With `image_missing`: `has`, the node the sandbox goes to has a template over the image now (its takeover is asked again a second later); `sending`, the node asked sends the image (asked again every few seconds); `stored`, it says the store has it (the takeover is asked again a second later; a minute later where that was said before). Null with no `holder`, the move asked again once a minute."
                  }
                }
              },
              {
                "type": "null"
              }
            ],
            "description": "Why it failed, of one that failed (the new node's refusal, where it was taken back to the old node); null, of one that is done; of one under way, what the step it is at was last told where it is asked again."
          },
          "attempts": {
            "type": "integer",
            "minimum": 0,
            "description": "How many times the step it is at was asked without an answer that settles it."
          },
          "next_attempt_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When that step is asked again; null where it is not waiting."
          },
          "batch": {
            "type": [
              "string",
              "null"
            ],
            "description": "The evacuation (`evc-…`) or recovery (`rcv-…`) it is of."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "ended_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "MoveNode": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id",
          "name"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": [
              "string",
              "null"
            ],
            "description": "Its name; null for a node that is off the books since."
          }
        }
      },
      "ComposeReport": {
        "type": "object",
        "additionalProperties": false,
        "description": "What an import does with a Compose model, item by item, and what it makes.",
        "required": [
          "object",
          "name",
          "dry_run",
          "strict",
          "profiles",
          "treatments",
          "items",
          "services",
          "volumes",
          "secrets",
          "left_out",
          "absent",
          "deploy"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "compose_import"
            ]
          },
          "name": {
            "type": [
              "string",
              "null"
            ],
            "description": "The model's `name`: each service's label `compose.project`. Null: it has none."
          },
          "dry_run": {
            "type": "boolean"
          },
          "strict": {
            "type": "boolean"
          },
          "profiles": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "treatments": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "done",
              "adapted",
              "ignored",
              "refused"
            ],
            "properties": {
              "done": {
                "type": "integer",
                "minimum": 0
              },
              "adapted": {
                "type": "integer",
                "minimum": 0
              },
              "ignored": {
                "type": "integer",
                "minimum": 0
              },
              "refused": {
                "type": "integer",
                "minimum": 0
              }
            },
            "description": "How many items of the report have each treatment."
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ComposeItem"
            },
            "description": "The model's top-level elements: `name`, each volume, network, config and secret, extensions."
          },
          "services": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ComposeService"
            },
            "description": "The services the import takes, in the model's order."
          },
          "volumes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ComposeVolume"
            },
            "description": "The volumes of the project the services mount."
          },
          "secrets": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ComposeSecret"
            },
            "description": "The secrets of the model that services are granted."
          },
          "left_out": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "name",
                "profiles"
              ],
              "properties": {
                "name": {
                  "type": "string"
                },
                "profiles": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                }
              }
            },
            "description": "The services of profiles that are not turned on, which the import does not touch."
          },
          "absent": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "The project's services that the model does not have: kept as they are."
          },
          "deploy": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^dpl-",
            "description": "The deploy the import made (`GET /v1/deploys/{id}`). Null: a dry run, or nothing would get a version."
          }
        }
      },
      "ComposeItem": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "at",
          "treatment",
          "why",
          "change"
        ],
        "properties": {
          "at": {
            "type": "string",
            "description": "Where in the model: of a service, its attribute (`ports[0]`, `depends_on.db`, `environment.KEY`), empty for the service as a whole; of the top level, the element (`volumes.db-data`)."
          },
          "treatment": {
            "type": "string",
            "enum": [
              "done",
              "adapted",
              "ignored",
              "refused"
            ],
            "description": "`done`: it means the same on the platform; `adapted`: the platform does the same another way; `ignored`: it matters to a local run alone (refused with `strict`); `refused`: the platform cannot do it, and doing without it would let the program go wrong quietly."
          },
          "why": {
            "type": "string"
          },
          "change": {
            "type": [
              "string",
              "null"
            ],
            "description": "Of one that is refused: what to change. Null otherwise."
          }
        }
      },
      "ComposeService": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "name",
          "app_id",
          "status",
          "kind",
          "stateful",
          "version",
          "items",
          "app"
        ],
        "properties": {
          "name": {
            "type": "string",
            "description": "The service's name, which is its app's."
          },
          "app_id": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^app-",
            "description": "Its app. Null: a dry run of a service the project does not have."
          },
          "status": {
            "type": "string",
            "enum": [
              "created",
              "changed",
              "unchanged"
            ],
            "description": "What the import does (or, in a dry run, would do) to its app: makes it, writes what changed, or nothing."
          },
          "kind": {
            "type": "string",
            "enum": [
              "service",
              "job"
            ],
            "description": "`job`: a service of the model waits for it to complete, and it runs to its end in each deploy that needs it."
          },
          "stateful": {
            "type": "boolean",
            "description": "It mounts a volume of its own: one instance, which has the volume."
          },
          "version": {
            "type": "boolean",
            "description": "Whether the import's deploy makes a version of it: what its version would be made of, its references resolved, is not what its current version is."
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ComposeItem"
            }
          },
          "app": {
            "$ref": "#/components/schemas/ComposeApp"
          }
        }
      },
      "ComposeApp": {
        "type": "object",
        "additionalProperties": false,
        "description": "The fields of the service's app as the import writes them: what its versions are made of, and what holds at once.",
        "required": [
          "image",
          "image_pull_secret",
          "start_command",
          "start_args",
          "port",
          "ready",
          "env",
          "secrets",
          "size",
          "volumes",
          "kind",
          "files",
          "user",
          "working_dir",
          "stop",
          "hostname",
          "regions",
          "scale",
          "keys",
          "egress",
          "metadata",
          "logs",
          "public",
          "depends_on",
          "network"
        ],
        "properties": {
          "image": {
            "$ref": "#/components/schemas/App/properties/image"
          },
          "image_pull_secret": {
            "$ref": "#/components/schemas/App/properties/image_pull_secret"
          },
          "start_command": {
            "$ref": "#/components/schemas/App/properties/start_command"
          },
          "start_args": {
            "$ref": "#/components/schemas/App/properties/start_args"
          },
          "port": {
            "$ref": "#/components/schemas/App/properties/port"
          },
          "ready": {
            "$ref": "#/components/schemas/App/properties/ready"
          },
          "env": {
            "$ref": "#/components/schemas/App/properties/env"
          },
          "secrets": {
            "$ref": "#/components/schemas/App/properties/secrets"
          },
          "size": {
            "$ref": "#/components/schemas/App/properties/size"
          },
          "volumes": {
            "$ref": "#/components/schemas/App/properties/volumes"
          },
          "drives": {
            "$ref": "#/components/schemas/App/properties/drives"
          },
          "key_drives": {
            "$ref": "#/components/schemas/App/properties/key_drives"
          },
          "kind": {
            "$ref": "#/components/schemas/App/properties/kind"
          },
          "files": {
            "$ref": "#/components/schemas/App/properties/files"
          },
          "user": {
            "$ref": "#/components/schemas/App/properties/user"
          },
          "working_dir": {
            "$ref": "#/components/schemas/App/properties/working_dir"
          },
          "stop": {
            "$ref": "#/components/schemas/App/properties/stop"
          },
          "hostname": {
            "$ref": "#/components/schemas/App/properties/hostname"
          },
          "runtime": {
            "$ref": "#/components/schemas/App/properties/runtime"
          },
          "regions": {
            "$ref": "#/components/schemas/App/properties/regions"
          },
          "scale": {
            "$ref": "#/components/schemas/App/properties/scale"
          },
          "keys": {
            "$ref": "#/components/schemas/App/properties/keys"
          },
          "egress": {
            "$ref": "#/components/schemas/App/properties/egress"
          },
          "metadata": {
            "$ref": "#/components/schemas/App/properties/metadata"
          },
          "logs": {
            "$ref": "#/components/schemas/App/properties/logs"
          },
          "traces": {
            "$ref": "#/components/schemas/App/properties/traces"
          },
          "public": {
            "$ref": "#/components/schemas/App/properties/public"
          },
          "depends_on": {
            "$ref": "#/components/schemas/App/properties/depends_on"
          },
          "network": {
            "$ref": "#/components/schemas/App/properties/network"
          }
        }
      },
      "App": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id",
          "object",
          "name",
          "image",
          "image_pull_secret",
          "start_command",
          "start_args",
          "port",
          "ready",
          "env",
          "secrets",
          "size",
          "regions",
          "scale",
          "keys",
          "egress",
          "metadata",
          "logs",
          "traces",
          "public",
          "volumes",
          "drives",
          "key_drives",
          "depends_on",
          "network",
          "kind",
          "files",
          "user",
          "working_dir",
          "stop",
          "hostname",
          "runtime",
          "private",
          "url",
          "direct",
          "direct_url",
          "checkpoint",
          "current_version",
          "deploying",
          "build_failures",
          "logs_state",
          "created_at",
          "updated_at",
          "managed_by"
        ],
        "properties": {
          "managed_by": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": false,
            "required": [
              "object",
              "id"
            ],
            "properties": {
              "object": {
                "type": "string",
                "enum": [
                  "database",
                  "sandboxes",
                  "installation"
                ]
              },
              "id": {
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "description": "What changes it alone, where something does. `object` `database`: the database it is under (`id`, `/v1/databases`); every other call that would change it is refused, 409 `managed_by_database`. `object` `sandboxes` (`id` null): the Sandboxes product's, made by the creates of the project's sandboxes (`POST /v1/sandboxes`): the project's default sandbox app, `sbx-sandbox` (its `image` null: each sandbox is made from the base template of the node it goes to, until the project names an image for it, `PATCH` with `image` alone, a new version made of that image, of which the creates that name no image are made from then on), or the app of an image a create named (`image`, as the create named it; `sbx-<the image's name>-<12 hex digits>`, one version). A version is ready from when it is made, and the app's instances are the project's sandboxes; every call of it but reading it and its versions, naming the default sandbox app's image and deleting it (its versions' templates go with it; the next create of its image, or of none, makes it again) is refused, 409 `managed_by_sandboxes`. Null: none's. `object` `installation`: the installation of a Managed Application it is part of (`id`, `/v1/installations`), which changes it alone: every other call that would change it is refused, 409 `managed_by_installation`."
          },
          "public": {
            "type": "boolean",
            "description": "Whether it answers at its default domain (`url`)."
          },
          "volumes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ServiceVolume"
            }
          },
          "drives": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ServiceDrive"
            },
            "description": "The Drives its instance mounts (`AppCreate.drives`): none, `[]`, where it names none."
          },
          "key_drives": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/KeyDrive"
            },
            "description": "The Drives each key's instance has of its own (`AppCreate.key_drives`): none, `[]`, where it gives none."
          },
          "depends_on": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ServiceDependency"
            }
          },
          "network": {
            "$ref": "#/components/schemas/ServiceNetwork"
          },
          "kind": {
            "type": "string",
            "x-extensible-enum": [
              "service",
              "job"
            ]
          },
          "files": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ServiceFile"
            }
          },
          "user": {
            "type": [
              "string",
              "null"
            ]
          },
          "working_dir": {
            "type": [
              "string",
              "null"
            ]
          },
          "stop": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/ServiceStop"
              },
              {
                "type": "null"
              }
            ]
          },
          "hostname": {
            "type": [
              "string",
              "null"
            ]
          },
          "runtime": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Runtime"
              },
              {
                "type": "null"
              }
            ],
            "description": "Its runtime settings (`AppCreate.runtime`). `null`: none."
          },
          "private": {
            "$ref": "#/components/schemas/ServicePrivate"
          },
          "id": {
            "type": "string",
            "pattern": "^app-[0-9a-hjkmnp-tv-z]{16}$"
          },
          "object": {
            "type": "string",
            "enum": [
              "app"
            ]
          },
          "name": {
            "type": "string"
          },
          "image": {
            "type": [
              "string",
              "null"
            ],
            "description": "What the app's next version is made of, with the seven fields after it: its newest version's, with what was changed since. `null`: the app has no image, and no version."
          },
          "image_pull_secret": {
            "type": [
              "string",
              "null"
            ],
            "description": "The name of the secret that holds the credentials of the image's registry. No answer has its value. `null`: none."
          },
          "start_command": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/StartCommand"
              },
              {
                "type": "null"
              }
            ]
          },
          "start_args": {
            "anyOf": [
              {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "minItems": 1,
                "maxItems": 1000
              },
              {
                "type": "null"
              }
            ],
            "description": "What follows the image's ENTRYPOINT in the place of its CMD. `null`: the image's CMD, or the whole of `start_command`."
          },
          "port": {
            "type": [
              "integer",
              "null"
            ]
          },
          "ready": {
            "$ref": "#/components/schemas/Ready"
          },
          "env": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "secrets": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "Names of secrets. No answer has a value."
          },
          "size": {
            "allOf": [
              {
                "$ref": "#/components/schemas/Size"
              }
            ],
            "required": [
              "vcpus",
              "memory_bytes"
            ]
          },
          "regions": {
            "type": "array",
            "items": {
              "type": "string",
              "x-extensible-enum": [
                "na",
                "eu",
                "ap"
              ]
            },
            "description": "In the order na, eu, ap."
          },
          "scale": {
            "allOf": [
              {
                "$ref": "#/components/schemas/Scale"
              }
            ],
            "required": [
              "min_instances",
              "max_instances",
              "concurrency",
              "sleep_after_ms",
              "sleep_notice_ms",
              "sleep_hold_max_ms",
              "delete_after_ms"
            ]
          },
          "keys": {
            "anyOf": [
              {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/AppKeys"
                  }
                ],
                "required": [
                  "from",
                  "delete_after_idle_ms",
                  "upgrade_on_deploy"
                ]
              },
              {
                "type": "null"
              }
            ]
          },
          "egress": {
            "$ref": "#/components/schemas/EgressRules",
            "description": "Egress rules as a sandbox has them (`default`, `allow`, `deny`, `http`, `inject`), null when there are none; kept as the calls gave them (a `PATCH` merges them key by key, as a node does). They hold at once and make no version: the nodes that serve the app put them on every instance of its group, the ones there are (one that runs for the connections it has too) and the ones made after; a keyed instance does not have them. The control plane checks only that they are an object of those keys of at most 256 KiB: what the rules say is a node's to check, when it applies them, and an instance whose rules its node cannot read is refused everything (the node's log says why)."
          },
          "metadata": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "logs": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "output",
              "keep",
              "requests"
            ],
            "description": "What the app's log has, each part as it was given or at its default (`AppLogSettings`).",
            "properties": {
              "output": {
                "type": "boolean"
              },
              "keep": {
                "type": "boolean"
              },
              "requests": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "sample_rate",
                  "keep_errors"
                ],
                "properties": {
                  "sample_rate": {
                    "type": "number",
                    "minimum": 0,
                    "maximum": 1
                  },
                  "keep_errors": {
                    "type": "boolean"
                  }
                }
              }
            }
          },
          "traces": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "sample_rate",
              "trust_incoming"
            ],
            "description": "What the app's requests' traces have, each part as it was given or at its default (`AppTraceSettings`): no span where `sample_rate` is 0.",
            "properties": {
              "sample_rate": {
                "type": "number",
                "minimum": 0,
                "maximum": 1
              },
              "trust_incoming": {
                "type": "boolean"
              }
            }
          },
          "url": {
            "type": [
              "string",
              "null"
            ],
            "description": "Where the app answers: `https://` and its default domain, `<name>-<the project's short name>.<the zone>`. It is the app's from the moment it is made (503 `app_not_deployed` until its first version is ready) and for as long as the app has its name. `null` on a control plane that is told no zone for apps (`--app-zone`)."
          },
          "direct": {
            "type": "boolean",
            "description": "Whether the app answers at a name that reaches the edges without the platform's proxy as well (`direct_url`): there a request has no limit of the proxy's on its body or on how long its answer may pause, and nothing but the edge's own limits protects the app."
          },
          "checkpoint": {
            "oneOf": [
              {
                "type": "object",
                "required": [
                  "every_seconds"
                ],
                "properties": {
                  "every_seconds": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 600
                  }
                },
                "additionalProperties": false
              },
              {
                "type": "null"
              }
            ],
            "description": "Checkpoints of its keyed instances while they run: every `every_seconds` (10 unless said, 1 to 600) an instance's memory and disk as they are at one instant go to its node's object store, and when its node's host is lost the instance is taken over on another node from its newest checkpoint with its processes, losing at most what it did since (an instance that sleeps is taken over from its sleep, which loses nothing). Its node pauses it for a few milliseconds for each; what it wrote since the last one is what is sent, compressed, and one that writes more than its node's link for checkpoints takes falls behind instead of filling it (the instance's `checkpoint`: `last_at`, `behind_seconds`, `bytes_per_second`, `stored_bytes`). What the store keeps of an instance's newest checkpoint is billed as sleeping state (`state`) while it runs; nothing for what is sent. Holds at once and makes no version: the instances that are there are given it, those made later are made with it. Not a stateful service's instance: its volume has no copy in the store. An instance's Drives (its own `key_drives` too) are pinned with its disk at the same instant, and a takeover brings them back as they were then; an instance with a volume that is not a Drive takes none, and its `checkpoint.error` says so. Null: none (the default)."
          },
          "direct_url": {
            "type": [
              "string",
              "null"
            ],
            "description": "Where the app answers without the proxy: its default domain's label under the platform's direct zone (`https://<name>-<project>.direct.glac.run`). Null unless it says `direct`, and where the platform has no proxy."
          },
          "current_version": {
            "type": [
              "string",
              "null"
            ],
            "description": "The id of the version the app's traffic goes to: the newest deploy that came up, or what a rollback named. `null` until a version is ready."
          },
          "deploying": {
            "type": [
              "string",
              "null"
            ],
            "description": "The id of the version that is being deployed and takes the traffic when it is ready."
          },
          "build_failures": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AppBuildFailure"
            },
            "description": "The nodes that are to serve the version the app's traffic goes to and could not build it when they were asked to after its deploy (a node that joined, one whose template of it is of no use any more, one that came back), one an item, by the node's id; empty where there are none. A node whose build failed is asked again ten minutes after its last try, and is no longer here once it has built the version; the other nodes serve the version meanwhile. Each failure is also an event, `app.build_failed`, once."
          },
          "logs_state": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "nodes"
            ],
            "description": "How the app's log is kept on each node that keeps one of it, as the node said in its last inventory (every few seconds).",
            "properties": {
              "nodes": {
                "type": "array",
                "description": "One item a node, by the node's id; empty where no node keeps a log of the app, or none says (a node of before 2026-10-24).",
                "items": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "node_id",
                    "for_good",
                    "reason",
                    "since"
                  ],
                  "properties": {
                    "node_id": {
                      "type": "string"
                    },
                    "for_good": {
                      "type": "boolean",
                      "description": "Whether what the app's instances print there now is kept for good: shipped to the object store, kept for the days the plan keeps, and metered. `false`: it is kept on that node alone, read as ever, not metered, and gone with the node (`logs.keep_off` in the log). Between the look of the control plane's that stops the account's logs (its plan's are used up and it holds nothing to pay for more) and a node's hearing of it, that node's is still `true`: what is printed there then is kept for good and metered, and with nothing to pay with it is written off (the statement's `written_off_usd`)."
                    },
                    "reason": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "enum": [
                        "setting",
                        "over_plan",
                        null
                      ],
                      "description": "Why it is not kept for good: `setting`, the app's `logs.keep`; `over_plan`, its account has used up the logs its plan includes and holds nothing to pay for more. null where it is."
                    },
                    "since": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "format": "date-time",
                      "description": "Since when it is so, by the node's clock. null: the node noted no time."
                    }
                  }
                }
              }
            }
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ServiceVolume": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "volume",
          "path",
          "read_only"
        ],
        "properties": {
          "volume": {
            "type": "string"
          },
          "path": {
            "type": "string"
          },
          "read_only": {
            "type": "boolean"
          },
          "nocopy": {
            "const": true,
            "description": "There only where it was given as `true`."
          }
        }
      },
      "ServiceDrive": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "drive",
          "path",
          "mode"
        ],
        "properties": {
          "drive": {
            "type": "string"
          },
          "path": {
            "type": "string"
          },
          "mode": {
            "type": "string",
            "x-extensible-enum": [
              "read_write"
            ]
          }
        }
      },
      "KeyDrive": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "name",
          "path",
          "size_bytes"
        ],
        "properties": {
          "name": {
            "type": "string"
          },
          "path": {
            "type": "string"
          },
          "size_bytes": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int64"
          },
          "nocopy": {
            "const": true,
            "description": "There only where it was given as `true`."
          }
        }
      },
      "ServiceDependency": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "service",
          "condition"
        ],
        "properties": {
          "service": {
            "type": "string"
          },
          "condition": {
            "type": "string",
            "x-extensible-enum": [
              "started",
              "ready",
              "completed"
            ]
          }
        }
      },
      "ServiceNetwork": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "aliases",
          "endpoint_mode"
        ],
        "properties": {
          "aliases": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "endpoint_mode": {
            "type": "string",
            "x-extensible-enum": [
              "vip",
              "dnsrr"
            ]
          }
        }
      },
      "ServiceFile": {
        "type": "object",
        "description": "A file written into its instances: its text (`content`) or the name of the secret whose value it holds (`secret`), never the value.",
        "additionalProperties": false,
        "required": [
          "path",
          "mode",
          "uid",
          "gid"
        ],
        "properties": {
          "path": {
            "type": "string"
          },
          "content": {
            "type": "string"
          },
          "secret": {
            "type": "string"
          },
          "mode": {
            "type": "integer"
          },
          "uid": {
            "type": "integer"
          },
          "gid": {
            "type": "integer"
          }
        }
      },
      "ServiceStop": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "signal",
          "grace_ms"
        ],
        "properties": {
          "signal": {
            "type": [
              "string",
              "null"
            ]
          },
          "grace_ms": {
            "type": [
              "integer",
              "null"
            ]
          }
        }
      },
      "Runtime": {
        "type": "object",
        "description": "A version's runtime settings as it keeps them (`RuntimeGiven`), every part given.",
        "additionalProperties": false,
        "required": [
          "ulimits",
          "sysctls",
          "tmpfs",
          "dns",
          "shm_size_bytes"
        ],
        "properties": {
          "ulimits": {
            "type": "object",
            "additionalProperties": {
              "$ref": "#/components/schemas/RuntimeLimitGiven"
            }
          },
          "sysctls": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "tmpfs": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "path",
                "size_bytes",
                "mode"
              ],
              "properties": {
                "path": {
                  "type": "string"
                },
                "size_bytes": {
                  "type": [
                    "integer",
                    "null"
                  ]
                },
                "mode": {
                  "type": "integer"
                }
              }
            }
          },
          "dns": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "servers",
              "search",
              "options"
            ],
            "properties": {
              "servers": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "search": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "options": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            }
          },
          "shm_size_bytes": {
            "type": [
              "integer",
              "null"
            ]
          }
        }
      },
      "RuntimeLimitGiven": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "soft",
          "hard"
        ],
        "properties": {
          "soft": {
            "type": "integer",
            "minimum": -1,
            "description": "No higher than `hard`; -1 is no limit."
          },
          "hard": {
            "type": "integer",
            "minimum": -1
          }
        }
      },
      "ServicePrivate": {
        "type": "object",
        "description": "Where the project's sandboxes on its network find the app: its address there, which it keeps for as long as it is there, renamed or not; and its name under `internal`. A sandbox of the project on the network finds it by its name, by `domain`, and by each of its `network.aliases` with and without `.internal`.",
        "additionalProperties": false,
        "required": [
          "address",
          "domain"
        ],
        "properties": {
          "address": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^10\\.(6[4-9]|[7-9][0-9]|1[01][0-9]|12[0-7])(\\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){2}$",
            "description": "An address of the project's ranges (`GET /v1/project`). `null` only where every range of the site is cut."
          },
          "domain": {
            "type": "string",
            "description": "`<name>.internal`."
          }
        }
      },
      "StartCommand": {
        "description": "A line for `sh -c`, or a list of arguments that is run as it is (for an image without a shell). At most 64 KiB, no NUL bytes.",
        "oneOf": [
          {
            "type": "string",
            "minLength": 1
          },
          {
            "type": "array",
            "items": {
              "type": "string"
            },
            "minItems": 1,
            "maxItems": 1000
          }
        ]
      },
      "Ready": {
        "type": "object",
        "description": "How to tell that an instance is up, which a version's template is saved after: exactly one of the three. The first two are asked from outside the instance, so the service has to listen on every address (`0.0.0.0`).",
        "additionalProperties": false,
        "minProperties": 1,
        "maxProperties": 1,
        "properties": {
          "http": {
            "type": "string",
            "description": "A path, beginning with `/`: a `GET` of it on the app's port is answered with a status from 200 to 399."
          },
          "tcp": {
            "type": "boolean",
            "enum": [
              true
            ],
            "description": "The app's port takes a connection."
          },
          "command": {
            "$ref": "#/components/schemas/StartCommand",
            "description": "A command that succeeds in the instance. It is run by the instance's shell: a list of arguments is written as one line for it."
          }
        }
      },
      "Size": {
        "type": "object",
        "description": "What one instance may use at most. It is billed by what it uses.",
        "additionalProperties": false,
        "properties": {
          "vcpus": {
            "type": "integer",
            "minimum": 1
          },
          "memory_bytes": {
            "type": "integer",
            "minimum": 134217728,
            "multipleOf": 2097152,
            "description": "An even number of MiB (a whole number of the 2 MiB pages a node loads a machine's memory in), in bytes. Any other size is 400 `invalid_field` (param `size.memory_bytes`): no node would make an instance of it, as none makes a sandbox of it (the node's `SandboxLimitsCreate.memory_bytes`)."
          }
        }
      },
      "Scale": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "min_instances": {
            "type": "integer",
            "minimum": 0,
            "description": "How many instances of the app are kept awake in a region, whether a request comes or not: the floor. The nodes of the region that serve the app share it as they share `max_instances` (two nodes and a floor of 2: one on each; a floor of 1: one, on the node that is tried first; a floor of 5: three and two), and a node that is away is made up for by the others. A node makes its part when it is told, not at the first request, and neither stops nor deletes those instances for being idle; they are metered as instances that are awake. 0, which is what it is when nothing is said: none. Lowered, the instances beyond it are idle from that moment, and are stopped and deleted as idle instances are. At most `max_instances`, where that is given, and at most what the account's plan lets an app ask for in a region (`caps.app_instances.limit`: 5, 50 and 500): more is refused, 403 `plan_cap_reached`."
          },
          "max_instances": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 1,
            "description": "How many instances the app has at the most in a region: the nodes of the region that serve the app share it. At most what the account's plan lets an app ask for (`caps.app_instances.limit`: 5 on Free, 500 on Paid): more is refused, 403 `plan_cap_reached`, and what an app asked for on a plan that let it is kept, and held to the plan's number. `null`: the plan's number for an app that names none (`caps.app_instances.default`: 2 on Free, 50 on Paid), or `min_instances` where that is more. What the nodes are told is no more than the account has room for awake beside everything else of it that is awake, nor less than the app has awake."
          },
          "concurrency": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 1,
            "maximum": 1000,
            "description": "A cap on what one instance has in hand at a time: the requests the edges sent it, and the connections it has open on its project's network (through its service's address, which the node shares out among the instances connection by connection, or to its own). An instance with that many is full, and another is made for more. `null`, which is what it is when nothing is said: no cap. A node then adds an instance when the ones it has are near their size in CPU or memory, and an app does not have to guess a number; a service that takes one request at a time gives one."
          },
          "sleep_after_ms": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "maximum": 600000,
            "description": "The most an instance waits without a request before it stops, in milliseconds. Below it, its node picks the wait by the app's own traffic: the wait that would have cost least over the app's last gaps between a request's end and the next one, a second up and idle against a stop and the resume after it in CPU time, so that an app whose requests come closer together than a stop pays for stays up through them, and one whose requests are far apart stops as soon as its last answer has ended (`0`: stops at once, always). `null`, which is what it is when nothing is said: the platform's most, three seconds. An app that said 100, the default before 2026-10-28, says null from then on. One that stays stopped is deleted by its node after `delete_after_ms`, and the next request that needs an instance gets a new one; the instances `min_instances` keeps awake are neither stopped nor deleted."
          },
          "sleep_notice_ms": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "maximum": 60000,
            "description": "How long before an instance stops for being idle the programs connected to its guest's `/run/sbx/awake` are sent the line `sleep N`, in milliseconds: they flush what they hold, close their connections, or raise a hold, which puts the stop off. The stop comes that long after, whether they answer or not; where nobody is connected, it is not put off. A keyed instance is told as well. 0 or null: not told (the default)."
          },
          "sleep_hold_max_ms": {
            "type": "integer",
            "minimum": 0,
            "maximum": 900000,
            "description": "How long the holds a program in an instance raises keep it up after its time idle has run out, in milliseconds: a program connects to `/run/sbx/awake` and sends `+` before it answers a request whose work goes on after the answer, and `-` (or closes the connection, or dies) when that work is done; the instance stops `sleep_after_ms` after the last hold is lowered. Past this time it stops whatever holds it, the programs connected told `sleep N cap` first where `sleep_notice_ms` asks for a notice. Held, it runs and is metered as running. 0: holds are not heeded. Null: 30000 (the default)."
          },
          "suspend_after_ms": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 1000,
            "maximum": 86400000,
            "description": "How long a stopped instance stays paused before its memory is saved and it sleeps for real, in milliseconds: paused, the next request is answered in a millisecond or two and its memory is metered; asleep, it wakes in a few more and only what it keeps is. Its node holds it to the longest it holds any instance paused (ten minutes); the host's memory running short, a slot another machine needs and a sleep somebody asks for put it to sleep sooner. Null (the default): its node's longest. A database's `suspend_after_seconds` is its app's."
          },
          "wake_daily_at": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^([01][0-9]|2[0-3]):[0-5][0-9]$",
            "description": "A time of day in UTC, `HH:MM`, at which the app's keyed instances and its stateful service's instance that rest are woken every day, for work of their own that waits for a wake (a database's maintenance, say); the instances the nodes make and delete by the app's requests are woken by requests alone. The wake is no activity: what runs then is, and once that is done the instance rests again. A stateful service's instance is woken besides for the next run of its PostgreSQL server's `pg_cron` jobs, whatever this says. Null (the default): none. A database's `wake_daily_at` is its app's."
          },
          "delete_after_ms": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 1000,
            "maximum": 3600000,
            "description": "After how long idle an instance is deleted, counted from where its stop is counted from: the next request that needs one gets a new instance, a clone of the version's template, without what the deleted one had gathered in its memory and on its disk (keep what is to last in a volume or a database). A second to an hour, the platform's time, which is the most: more is refused (400 `invalid_field`), and an instance that is to stay warm is one of `min_instances`. `null`, which is what it is when nothing is said: the hour of the nodes (`--app-delete-after-seconds`). A change holds for the instances the app has as for the ones made after: each node judges every instance by the app's time as it is, once a second at the least. Not for the app's keyed instances, which `keys.delete_after_idle_ms` is for."
          }
        }
      },
      "AppKeys": {
        "type": "object",
        "description": "What an app says of its keyed instances. In a `PATCH` it is merged a part at a time, `null` taking a part away; an app whose two parts are `null` says nothing of keys, and its `keys` is `null`.",
        "additionalProperties": false,
        "properties": {
          "from": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/AppKeysFrom"
              },
              {
                "type": "null"
              }
            ],
            "description": "Where a request for the app carries its key. A request that carries a key goes to the key's instance (made for it where the key names none: `POST /v1/apps/{app}/instances`), and one that carries none goes as any request for the app does. A change holds at once, for the requests that come after it. `null`: requests carry none, and keys are named through `/v1/apps/{app}/instances` alone."
          },
          "upgrade_on_deploy": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "Every deploy of the app makes a batch of upgrades of its keys' instances to the new version, each once it sleeps (`POST /v1/apps/{app}/upgrades` with its defaults; slice U4), which stops the batch an earlier deploy made, so the keys follow the newest. `false` (`null` is the same), which is what it is when nothing is said: a key's instance stays on its version until it is upgraded."
          },
          "delete_after_idle_ms": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 1000,
            "description": "After how long asleep a keyed instance is deleted, in milliseconds, by its node's count of its sleep (`idle_since` of its object): its key is freed, and the next request that carries the key makes a new instance, from the version the app's traffic goes to then; the event `sandbox.deleted` says `reason: idle` and the key. A second at the least, and no most. `null`, which is what it is when nothing is said: never, a keyed instance is the key's until it is deleted. A change holds for the keyed instances the app has: they are looked at again by the app's time as it is, at once, and every minute or as often as the shortest time an app gives, a second apart at the least. An instance with a `delete_after_idle_ms` of its own (`PATCH /v1/sandboxes/{id}`) goes by that one instead, longer or shorter: its node deletes it, and its key is freed when the control plane hears of it. A frozen instance (a spend limit; its account's plan, balance or debt) is deleted by neither while it is frozen: once let go of, the sleep it is in counts from then."
          }
        }
      },
      "AppKeysFrom": {
        "type": "object",
        "additionalProperties": false,
        "minProperties": 1,
        "maxProperties": 1,
        "properties": {
          "header": {
            "type": "string",
            "description": "A request header: its value is the key."
          },
          "path_segment": {
            "type": "integer",
            "minimum": 1,
            "maximum": 32,
            "description": "A segment of the path, 1 for the first, decoded (`%20` is a space): it is the key, and the request goes to the instance with its path as it came."
          }
        }
      },
      "EgressRules": {
        "type": [
          "object",
          "null"
        ],
        "description": "Egress rules as a request gives them (see `Egress`). Every key may be left out. At creation a key left out is `allow` for `default` and empty for a list. In a PATCH the object is merged into the sandbox's rules: a key that is given replaces what was there (a list is replaced whole), a key given as null goes back to nothing, a key left out stays. Null for the whole field takes every rule away. An `allow` list under `default: allow` would do nothing and is refused, and so is an `allow` entry that lies wholly within the ranges no sandbox reaches. Each list takes at most 1000 entries, `http` and `inject` at most 100 hosts each.",
        "properties": {
          "default": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "allow",
              "deny",
              null
            ]
          },
          "allow": {
            "type": [
              "array",
              "null"
            ],
            "maxItems": 1000,
            "items": {
              "$ref": "#/components/schemas/EgressEntry"
            }
          },
          "deny": {
            "type": [
              "array",
              "null"
            ],
            "maxItems": 1000,
            "items": {
              "$ref": "#/components/schemas/EgressEntry"
            }
          },
          "http": {
            "type": [
              "array",
              "null"
            ],
            "maxItems": 100,
            "items": {
              "$ref": "#/components/schemas/EgressHttp"
            }
          },
          "inject": {
            "type": [
              "array",
              "null"
            ],
            "maxItems": 100,
            "items": {
              "$ref": "#/components/schemas/EgressInject"
            }
          },
          "deny_ports": {
            "type": [
              "array",
              "null"
            ],
            "maxItems": 64,
            "items": {
              "type": "integer",
              "minimum": 1,
              "maximum": 65535
            },
            "description": "Ports no connection reaches, TCP or UDP, wherever it goes and whatever else the rules say. A Browsers session's always hold 25, 465, 587 and the ports mining pools listen on (3333, 4444, 5555, 7777, 14444, 45700): the control plane adds them to whatever a change of its rules gives."
          }
        },
        "additionalProperties": false
      },
      "EgressEntry": {
        "type": "string",
        "maxLength": 300,
        "description": "A host name (`pypi.org`), everything under a name (`*.github.com`: `api.github.com` and `a.b.github.com`, not `github.com` itself), an IPv4 or IPv6 address, or a range (`140.82.112.0/20`). Names are lower-cased; one with letters outside ASCII is given in its `xn--` form. No port. Sandboxes have no IPv6 today: IPv6 entries are kept and mean nothing yet."
      },
      "EgressHttp": {
        "type": "object",
        "description": "The requests a sandbox may send to one host. A request to the host passes when one of the rules in `allow` has its method and a pattern that matches its path; every other request to the host is refused by the node, which answers it itself (403 `egress_denied`, in `x-egress-proxy`). The first entry of `http` that speaks of a host is the one that holds for it; an entry that an earlier one speaks for (the same host, a host under an earlier `*.name`) is refused, so that none is there without effect. There is no list of requests to refuse: a path that is refused by name can be asked for under another name wherever the host reads paths more loosely than the node does.\n\nTo judge a request the node has to read it. TLS that names the host is ended by the node, on every port, under a certificate of its own authority (as for `inject`: the sandbox is made to trust it, and a client that does not ends the handshake), and each request is sent on over the node's own TLS connection to an address the node resolved for the host, once the host has shown a certificate for its name. A plain HTTP connection that names the host is read and judged the same. The node speaks HTTP/1.1 only, to the sandbox and to the host: a client that offers HTTP/2 as well is given HTTP/1.1, one that offers nothing else does not get through the handshake, and gRPC does not work to such a host. A request is passed on exactly as it was judged; what the node cannot read to its end in one way is refused (400 `malformed_request`): two `Content-Length` headers or one that is not plain digits, a `Transfer-Encoding` that is not `chunked` to the letter or stands beside a length, a chunk size that is no plain hex number, a `Content-Length`, `Transfer-Encoding` or `Host` with a tab or more than one space beside its value, lines that do not end in CRLF, folded headers, HTTP/1.0. The request's Host header has to be the host's name itself, with the connection's port or with none, and its target a path (400 `host_mismatch`). `CONNECT` is refused (400 `method_not_allowed`). The only upgrade that is passed on is a WebSocket's, asked for by the first request of a connection: what the client sends behind that request is held back until the host has answered `101`. A request that asks the host to take it for another method (`X-HTTP-Method-Override`, `X-HTTP-Method`, `X-Method-Override`) is judged as that method too.\n\nThe node reads requests and not answers, so it can answer only on a connection that has carried no request yet: a request that is refused later on a kept connection ends the connection, after the answers on their way, and the client that asks again on a new one gets the 403. Rules that change hold from the next request, also on connections that are open; a WebSocket, which has no next request, is ended when the rules would no longer pass on the request it was made with. Every refused request is counted (`usage.egress_refused_requests`).\n\nNothing that is not such a request goes to an address the node knows only as this host's: no other protocol, and nothing that is not TCP. For a sandbox with any `http` rule, UDP to port 443 is refused wherever it goes (HTTP over QUIC is nothing the node can read), and its DNS questions are answered by the node. Under `default: allow` this, like a `deny` list, holds software that means no harm and is no wall: the host can be reached through anything allowed that relays, and by its address under another name. Under `default: deny` it is meant to hold, with these exceptions: an address that `allow` lists is reached whatever is sent there; a host that shares a server with a host that is allowed whole can be asked through that host's name; and a host that takes the method or the path from somewhere else than the request line, or answers a request before it has read its body and then reads on, is not held by rules about methods and paths.",
        "required": [
          "host",
          "allow"
        ],
        "properties": {
          "host": {
            "type": "string",
            "maxLength": 300,
            "description": "A host name, or `*.` and a name. Not an address."
          },
          "allow": {
            "type": "array",
            "minItems": 1,
            "maxItems": 32,
            "items": {
              "$ref": "#/components/schemas/EgressHttpRule"
            },
            "description": "The requests that pass. A host that is to get none goes in `deny`."
          }
        },
        "additionalProperties": false
      },
      "EgressHttpRule": {
        "type": "object",
        "description": "Requests that pass: those with one of `methods` on a path that one of `paths` matches. At least one of the two keys; the one left out (or null) stands for every method, or every path.",
        "minProperties": 1,
        "properties": {
          "methods": {
            "type": [
              "array",
              "null"
            ],
            "minItems": 1,
            "maxItems": 16,
            "items": {
              "type": "string",
              "maxLength": 32
            },
            "description": "Methods, as tokens; kept in capitals. A request's method is compared as it was sent, so `get` is none of them. `CONNECT` cannot be allowed."
          },
          "paths": {
            "type": [
              "array",
              "null"
            ],
            "minItems": 1,
            "maxItems": 32,
            "items": {
              "type": "string",
              "maxLength": 1024
            },
            "description": "Patterns. A pattern is a path: it begins with `/`, has no `?` and no `#`, and is compared with the whole path of the request, which is its target before the `?` (the query is not judged). `*` stands for any run of characters, `/` included, and nothing else is special: `/repos/*` is everything under `/repos/` and not `/repos`. Case matters. Both are compared in one form: escapes of unreserved characters are decoded (`%7E` is `~`), other escapes are kept with their digits in capitals, bytes outside ASCII are escaped. An encoded slash stays one and is not `/`; a pattern can contain `%2F` to allow exactly that (`/@types%2Fnode`). A path that hosts could read as another path has no form and matches no pattern (a rule without `paths` still has it): one with a segment that is `.` or `..` after its escapes are decoded, once or more often, with `\\` and an encoded slash read as `/`, with what follows a `;` in a segment cut off and the spaces at a segment's ends dropped; one with a malformed escape, a `#`, a control character, bytes that are no UTF-8, or a character that folds into `.`, `/` or `\\` (U+FF0E, U+FF0F, U+FF3C, U+2215, U+2044, U+2024); one of more than 8192 bytes. Such a request is answered 400 `malformed_request`."
          }
        },
        "additionalProperties": false
      },
      "EgressInject": {
        "type": "object",
        "description": "Headers for one host. For a TLS connection to the host on port 443 the node ends the sandbox's TLS itself, under a certificate of its own authority (which a sandbox with `inject` rules is made to trust: its certificate is in the system's store and at `/etc/sbx/egress-ca.pem`), adds the headers to each HTTP/1.1 request, in place of any the sandbox sent under those names, and sends the request to the host over a TLS connection of its own, after the host has shown a certificate for its name that the web's authorities vouch for. The request's Host header has to be the host, and its target a path. A request that is not inside TLS is passed on as it is, without the headers. `TRACE` and `CONNECT` are refused. Requests on such a connection are read as strictly as those to a host in `http` (see `EgressHttp`): HTTP/1.1 only, and no upgrade but a WebSocket's, asked for by the first request of a connection. The node's own answers on such a connection are in `x-egress-proxy`.",
        "required": [
          "host",
          "headers"
        ],
        "properties": {
          "host": {
            "type": "string",
            "maxLength": 300,
            "description": "A host name, or `*.` and a name. Not an address."
          },
          "headers": {
            "type": "object",
            "minProperties": 1,
            "maxProperties": 20,
            "description": "Each header's name and value. In a value, `${secrets.NAME}` stands for the secret of that name (`PUT /v1/secrets/{name}`); the sandbox object shows the reference, never the secret. Headers that say where a request goes or where it ends (`Host`, `Content-Length`, `Transfer-Encoding`, `Connection`, `Upgrade`, `TE`, `Trailer`, `Keep-Alive`, `Expect`) cannot be set.",
            "additionalProperties": {
              "type": "string",
              "maxLength": 8192
            }
          }
        },
        "additionalProperties": false
      },
      "AppBuildFailure": {
        "type": "object",
        "description": "A node's failed build of the version an app's traffic goes to, after its deploy. The most frequent cause is the secret the version pulls its image with: what it holds then is what the node is given, and a secret that was deleted fails the build (`secret_not_found`), as does a token in it that has expired (the registry refuses it: `registry_auth_failed`). An ECR token expires twelve hours after it was made, and nothing renews it: whoever pulls from ECR puts a new token in the secret before that (`PUT /v1/secrets/{name}`).",
        "additionalProperties": false,
        "required": [
          "node_id",
          "version_id",
          "error",
          "failed_at"
        ],
        "properties": {
          "node_id": {
            "type": "string",
            "description": "The node that could not build it."
          },
          "version_id": {
            "type": "string",
            "pattern": "^ver-[0-9a-hjkmnp-tv-z]{16}$"
          },
          "error": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "code",
              "message"
            ],
            "properties": {
              "code": {
                "type": "string",
                "description": "The node's code, or this control plane's (`secret_not_found`, `secret_unreadable`, `invalid_field`, `secret_host_not_allowed`) where the version's secrets could not be read, or may not go to its image's registry."
              },
              "message": {
                "type": "string",
                "description": "The words of the node, or of the registry, which it passes on without the credentials; or this control plane's, which name the secret."
              }
            }
          },
          "failed_at": {
            "type": "string",
            "format": "date-time",
            "description": "When its last try failed."
          }
        }
      },
      "ComposeVolume": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "model",
          "name",
          "id",
          "shared",
          "status",
          "used_by"
        ],
        "properties": {
          "model": {
            "type": [
              "string",
              "null"
            ],
            "description": "Its name in the model. Null: made for a path a stateful service mounts with no volume named."
          },
          "name": {
            "type": "string",
            "description": "Its name in the project."
          },
          "id": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^vol-",
            "description": "Null: not made yet (`to_make`)."
          },
          "shared": {
            "type": "boolean",
            "description": "Several services mount it, or one with several instances: every instance of each mounts it at once."
          },
          "status": {
            "type": "string",
            "enum": [
              "to_make",
              "made",
              "there",
              "external"
            ],
            "description": "`to_make`: the import makes it (a dry run says so of what it would make); `made`: this import made it; `there`: an earlier import made it, or a volume of its name was there; `external`: the model names it `external`, and the project has it."
          },
          "used_by": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "ComposeSecret": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "model",
          "name",
          "there",
          "used_by"
        ],
        "properties": {
          "model": {
            "type": "string",
            "description": "Its name in the model."
          },
          "name": {
            "type": "string",
            "description": "The project's secret that holds it: the model's name, with each character a secret's name does not take written as an underscore."
          },
          "there": {
            "type": "boolean",
            "description": "Whether the project has it. An import (not a dry run) with one that is not there is refused 404 `secret_not_found`."
          },
          "used_by": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "VerifyEmail": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "token"
        ],
        "properties": {
          "token": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200,
            "description": "The token of the link (`ev_...`)."
          }
        }
      },
      "AuthProviderList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "data"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AuthProvider"
            }
          }
        }
      },
      "AuthProvider": {
        "type": "object",
        "additionalProperties": false,
        "description": "A provider users sign in with.",
        "required": [
          "object",
          "id",
          "name"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "provider"
          },
          "id": {
            "type": "string",
            "x-extensible-enum": [
              "github",
              "google"
            ]
          },
          "name": {
            "type": "string",
            "description": "As people call it: GitHub, Google."
          }
        }
      },
      "OAuthStart": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "next": {
            "type": "string",
            "pattern": "^/a/",
            "maxLength": 500,
            "description": "The console's page to go to after the sign-in: one of an account's (`/a/...`), as the sign-in page's own `next`."
          }
        }
      },
      "OAuthStarted": {
        "type": "object",
        "additionalProperties": false,
        "description": "A sign-in (or a link) sent to a provider.",
        "required": [
          "object",
          "provider",
          "url",
          "expires_at"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "oauth_start"
          },
          "provider": {
            "type": "string",
            "x-extensible-enum": [
              "github",
              "google"
            ]
          },
          "url": {
            "type": "string",
            "description": "The provider's page to send the browser to, with the sign-in's `state`, PKCE challenge and (Google) nonce."
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "description": "Ten minutes from now: the provider's answer is not taken after it."
          }
        }
      },
      "AccountEmailVerify": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "token"
        ],
        "properties": {
          "token": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200,
            "description": "The token of the link mailed to the address an account set (`av_...`)."
          }
        }
      },
      "AccountEmailVerified": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "account_id",
          "email",
          "verified"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "account_email"
          },
          "account_id": {
            "type": "string"
          },
          "email": {
            "type": "string",
            "description": "The address, as the link was mailed to it."
          },
          "verified": {
            "type": "boolean",
            "const": true
          }
        }
      },
      "EmailConfirm": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "token"
        ],
        "properties": {
          "token": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200,
            "description": "The token of the link mailed to the new address (`ec_...`)."
          }
        }
      },
      "InvitationAccept": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "token"
        ],
        "properties": {
          "token": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200,
            "description": "The token of the invitation's link (`in_...`)."
          }
        }
      },
      "InvitationPreview": {
        "type": "object",
        "additionalProperties": false,
        "description": "An invitation, as whoever has its link reads it before it is accepted.",
        "required": [
          "object",
          "account_id",
          "account_name",
          "email",
          "role",
          "invited_by",
          "by_operator",
          "expires_at"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "invitation_preview"
          },
          "account_id": {
            "type": "string",
            "pattern": "^acct-[0-9a-hjkmnp-tv-z]{16}$"
          },
          "account_name": {
            "type": "string"
          },
          "email": {
            "type": "string",
            "description": "The address invited."
          },
          "role": {
            "type": "string",
            "x-extensible-enum": [
              "owner",
              "admin",
              "member"
            ]
          },
          "invited_by": {
            "type": [
              "string",
              "null"
            ],
            "description": "The address of the member who invited; null: the platform's operator, or a member since deleted."
          },
          "by_operator": {
            "type": "boolean",
            "description": "The platform's operator invited (`POST /internal/accounts/{id}/owners`)."
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "SignIn": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "email",
          "password"
        ],
        "properties": {
          "email": {
            "type": "string"
          },
          "password": {
            "type": "string",
            "maxLength": 256
          }
        }
      },
      "Session": {
        "type": "object",
        "additionalProperties": false,
        "description": "A session a sign-in made, and its user.",
        "required": [
          "object",
          "id",
          "created_at",
          "expires_at",
          "user"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "session"
          },
          "id": {
            "type": "string",
            "pattern": "^ses-[0-9a-hjkmnp-tv-z]{16}$"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "description": "Thirty days from now: a session lasts thirty days from its last use."
          },
          "user": {
            "$ref": "#/components/schemas/User"
          }
        }
      },
      "PasswordReset": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "email"
        ],
        "properties": {
          "email": {
            "type": "string"
          }
        }
      },
      "PasswordResetAsked": {
        "type": "object",
        "additionalProperties": false,
        "description": "A new password by mail, asked for.",
        "required": [
          "object",
          "email"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "password_reset"
          },
          "email": {
            "type": "string",
            "description": "The address, as it is kept."
          }
        }
      },
      "PasswordResetConfirm": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "token",
          "password"
        ],
        "properties": {
          "token": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200,
            "description": "The token of the link (`pr_...`)."
          },
          "password": {
            "type": "string",
            "minLength": 10,
            "maxLength": 256,
            "description": "The new password: 10 to 256 characters, not the address."
          }
        }
      },
      "Me": {
        "type": "object",
        "additionalProperties": false,
        "description": "The signed-in user, the accounts it is a member of, and its session.",
        "required": [
          "object",
          "id",
          "email",
          "verified",
          "created_at",
          "password_changed_at",
          "has_password",
          "accounts",
          "session"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "user"
          },
          "id": {
            "type": "string",
            "pattern": "^usr-[0-9a-hjkmnp-tv-z]{16}$"
          },
          "email": {
            "type": "string",
            "description": "The address, in lower case."
          },
          "verified": {
            "type": "boolean",
            "description": "Whether the address is verified: by the link of its mail (`POST /v1/auth/verify`), by an invitation that came to it, or by a new password chosen through a mail."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "password_changed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the password was last chosen; null: the user has none (GitHub or Google made it, and it has chosen none since)."
          },
          "has_password": {
            "type": "boolean",
            "description": "Whether the user has a password: one GitHub or Google made has none until it chooses one by a mail's link (`POST /v1/auth/reset`)."
          },
          "accounts": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/UserAccount"
            },
            "description": "The accounts it is a member of, with its role in each, the one it joined first first."
          },
          "session": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "expires_at"
            ],
            "properties": {
              "id": {
                "type": "string",
                "pattern": "^ses-[0-9a-hjkmnp-tv-z]{16}$"
              },
              "expires_at": {
                "type": "string",
                "format": "date-time",
                "description": "Thirty days after it was last used: now."
              }
            }
          }
        }
      },
      "UserAccount": {
        "type": "object",
        "additionalProperties": false,
        "description": "An account a user is a member of, and its role there.",
        "required": [
          "id",
          "name",
          "role"
        ],
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^acct-[0-9a-hjkmnp-tv-z]{16}$"
          },
          "name": {
            "type": "string"
          },
          "role": {
            "type": "string",
            "x-extensible-enum": [
              "owner",
              "admin",
              "member"
            ]
          }
        }
      },
      "UserDelete": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "password": {
            "type": "string",
            "maxLength": 256,
            "description": "The user's password, where it has one."
          }
        }
      },
      "PasswordChange": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "current_password",
          "password"
        ],
        "properties": {
          "current_password": {
            "type": "string",
            "maxLength": 256
          },
          "password": {
            "type": "string",
            "minLength": 10,
            "maxLength": 256,
            "description": "The new password: 10 to 256 characters, not the address."
          }
        }
      },
      "VerificationAsked": {
        "type": "object",
        "additionalProperties": false,
        "description": "Another mail that verifies the address, sent.",
        "required": [
          "object",
          "email"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "verification"
          },
          "email": {
            "type": "string"
          }
        }
      },
      "Member": {
        "type": "object",
        "additionalProperties": false,
        "description": "A member of an account.",
        "required": [
          "object",
          "account_id",
          "user_id",
          "email",
          "role",
          "verified",
          "joined_at"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "member"
          },
          "account_id": {
            "type": "string",
            "pattern": "^acct-[0-9a-hjkmnp-tv-z]{16}$"
          },
          "user_id": {
            "type": "string",
            "pattern": "^usr-[0-9a-hjkmnp-tv-z]{16}$"
          },
          "email": {
            "type": "string"
          },
          "role": {
            "type": "string",
            "x-extensible-enum": [
              "owner",
              "admin",
              "member"
            ]
          },
          "verified": {
            "type": "boolean",
            "description": "Whether the member's address is verified."
          },
          "joined_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "EmailChangeRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "email"
        ],
        "properties": {
          "email": {
            "type": "string",
            "description": "The new address."
          },
          "password": {
            "type": "string",
            "maxLength": 256,
            "description": "The user's password, where it has one."
          }
        }
      },
      "EmailChange": {
        "type": "object",
        "additionalProperties": false,
        "description": "A new address asked for: mailed a link.",
        "required": [
          "object",
          "email",
          "expires_at"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "email_change"
          },
          "email": {
            "type": "string",
            "description": "The new address, in lower case."
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "description": "24 hours from now: the link opens nothing after it."
          }
        }
      },
      "AccountRename": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "name"
        ],
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100,
            "description": "1 to 100 characters, none of them a control character."
          }
        }
      },
      "ConsoleAccount": {
        "type": "object",
        "additionalProperties": false,
        "description": "An account as a member of it is shown it.",
        "required": [
          "object",
          "id",
          "name",
          "created_at",
          "role",
          "plan"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "account"
          },
          "id": {
            "type": "string",
            "pattern": "^acct-[0-9a-hjkmnp-tv-z]{16}$"
          },
          "name": {
            "type": "string"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "role": {
            "type": "string",
            "x-extensible-enum": [
              "owner",
              "admin",
              "member"
            ],
            "description": "The signed-in user's role in it."
          },
          "plan": {
            "type": "string",
            "x-extensible-enum": [
              "free",
              "paid"
            ],
            "description": "The plan it is on now, which every member sees (billing, its owners' alone, says the rest): what the console offers is held to it (on Free, a database rests five minutes, fixed)."
          }
        }
      },
      "UserProviderList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "data"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/UserProvider"
            }
          }
        }
      },
      "UserProvider": {
        "type": "object",
        "additionalProperties": false,
        "description": "A provider's user that signs in as the user.",
        "required": [
          "object",
          "provider",
          "name",
          "login",
          "email",
          "linked_at",
          "last_used_at"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "user_provider"
          },
          "provider": {
            "type": "string",
            "x-extensible-enum": [
              "github",
              "google"
            ]
          },
          "name": {
            "type": "string"
          },
          "login": {
            "type": "string",
            "description": "What the provider calls its user: a GitHub login, a Google account's address; as it was at its last sign-in."
          },
          "email": {
            "type": [
              "string",
              "null"
            ],
            "description": "The address the provider gave when the link was made, if it gave one."
          },
          "linked_at": {
            "type": "string",
            "format": "date-time"
          },
          "last_used_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When it last signed in; null: not since it was linked."
          }
        }
      },
      "ProviderLink": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "password": {
            "type": "string",
            "maxLength": 256,
            "description": "The user's password, where it has one."
          }
        }
      },
      "ConnectionList": {
        "type": "object",
        "additionalProperties": false,
        "description": "The user's connected apps, the newest first.",
        "required": [
          "data"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Connection"
            }
          }
        }
      },
      "Connection": {
        "type": "object",
        "additionalProperties": false,
        "description": "An app a user connected with OAuth.",
        "required": [
          "object",
          "id",
          "client",
          "account",
          "projects",
          "scopes",
          "created_at",
          "granted_at",
          "last_used_at"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "connection"
          },
          "id": {
            "type": "string",
            "pattern": "^con-[0-9a-hjkmnp-tv-z]{16}$"
          },
          "client": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "name",
              "kind"
            ],
            "properties": {
              "id": {
                "type": "string",
                "description": "Its client id at the authorization server: Hydra's, or its metadata document's address."
              },
              "name": {
                "type": "string"
              },
              "kind": {
                "type": "string",
                "x-extensible-enum": [
                  "registered",
                  "document",
                  "operator"
                ],
                "description": "How it came to the authorization server: it registered itself (RFC 7591: Glac has not checked who made it), its id is its metadata document's address, or the operator made it."
              }
            }
          },
          "account": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "name"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": [
                  "string",
                  "null"
                ]
              }
            }
          },
          "projects": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "id",
                "name"
              ],
              "properties": {
                "id": {
                  "type": "string"
                },
                "name": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "description": "Null of one the account no longer has."
                }
              }
            }
          },
          "scopes": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "The scopes of the API the user granted."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "granted_at": {
            "type": "string",
            "format": "date-time",
            "description": "When it was last granted: a later consent to the same app grants in place of the one before."
          },
          "last_used_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "ConnectRequest": {
        "type": "object",
        "additionalProperties": false,
        "description": "A consent the authorization server asks the user for, as the console's page shows it.",
        "required": [
          "object",
          "challenge",
          "client",
          "redirect_host",
          "loopback",
          "scopes",
          "audience",
          "user"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "connect_request"
          },
          "challenge": {
            "type": "string"
          },
          "client": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "name",
              "kind",
              "uri"
            ],
            "properties": {
              "id": {
                "type": "string",
                "description": "Its client id at the authorization server: Hydra's, or its metadata document's address."
              },
              "name": {
                "type": "string"
              },
              "kind": {
                "type": "string",
                "x-extensible-enum": [
                  "registered",
                  "document",
                  "operator"
                ],
                "description": "How it came to the authorization server: it registered itself (RFC 7591: Glac has not checked who made it), its id is its metadata document's address, or the operator made it."
              },
              "uri": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Its address, as it says (an https address), unchecked."
              }
            }
          },
          "redirect_host": {
            "type": [
              "string",
              "null"
            ],
            "description": "The host (and port) the browser is sent back to."
          },
          "loopback": {
            "type": "boolean",
            "description": "Whether that is the user's own machine."
          },
          "scopes": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "scope",
                "description",
                "grantable"
              ],
              "properties": {
                "scope": {
                  "type": "string"
                },
                "description": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "grantable": {
                  "type": "boolean"
                }
              }
            }
          },
          "audience": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "What every token is for: the API, and the MCP endpoint."
          },
          "user": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "email"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "email": {
                "type": "string"
              }
            }
          }
        }
      },
      "ConnectAccept": {
        "type": "object",
        "additionalProperties": false,
        "description": "A consent: the account, its projects and the scopes granted.",
        "required": [
          "account",
          "projects",
          "scopes"
        ],
        "properties": {
          "account": {
            "type": "string"
          },
          "projects": {
            "type": "array",
            "minItems": 1,
            "maxItems": 100,
            "items": {
              "type": "string"
            }
          },
          "scopes": {
            "type": "array",
            "minItems": 1,
            "maxItems": 100,
            "items": {
              "type": "string"
            }
          }
        }
      },
      "ConnectAnswer": {
        "type": "object",
        "additionalProperties": false,
        "description": "Where the browser goes next, back to the authorization server; and the connection, of a consent.",
        "required": [
          "object",
          "redirect_to"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "connect_answer"
          },
          "redirect_to": {
            "type": "string"
          },
          "connection": {
            "$ref": "#/components/schemas/Connection"
          }
        }
      },
      "UserAuditList": {
        "type": "object",
        "additionalProperties": false,
        "description": "Entries of the users' record, the newest first.",
        "required": [
          "data",
          "next_cursor"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/UserAuditEntry"
            }
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Where the next page begins; null on the last."
          }
        }
      },
      "UserAuditEntry": {
        "type": "object",
        "additionalProperties": false,
        "description": "An entry of the users' record: what happened to a user, or to an account's members (control/README.md, 记下来的).",
        "required": [
          "object",
          "id",
          "created_at",
          "action",
          "user_id",
          "email",
          "account_id",
          "by_user_id",
          "data",
          "remote_address",
          "request_id"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "user_audit_entry"
          },
          "id": {
            "type": "string",
            "pattern": "^uau-[0-9a-hjkmnp-tv-z]{16}$"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "action": {
            "type": "string",
            "x-extensible-enum": [
              "signed_up",
              "sign_up_repeated",
              "verified",
              "signed_in",
              "sign_in_failed",
              "reset_asked",
              "password_reset",
              "password_changed",
              "password_change_failed",
              "verification_asked",
              "signed_out",
              "session_ended",
              "sessions_ended",
              "locked",
              "unlocked",
              "invited",
              "invitation_revoked",
              "joined",
              "role_changed",
              "removed",
              "left",
              "owner_attached",
              "account_renamed",
              "project_made",
              "account_closed",
              "app_connected",
              "app_refused",
              "app_disconnected"
            ]
          },
          "user_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "The user it is about; null of what is about an account alone (an invitation, a renaming, a project, a closing)."
          },
          "email": {
            "type": [
              "string",
              "null"
            ],
            "description": "The address it is about: the user's, or the invited one."
          },
          "account_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "The account it is about, of what is about one."
          },
          "by_user_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Who did it, where that is not the user itself: another user's id, or `operator`."
          },
          "data": {
            "type": "object",
            "additionalProperties": true,
            "description": "What else it says: of `signed_in`, `session_id`; of `sign_in_failed`, `reason`; of `reset_asked`, whether it was `mailed`; of `password_reset`, `password_changed`, `sessions_ended` and of a lock, how many sessions ended; of `invited`, `invitation_id` and `role`; of `role_changed` and `account_renamed`, `from` and `to`; of `project_made`, `project_id` and `name`; of `account_closed`, the keys, members, invitations and domains it ended."
          },
          "remote_address": {
            "type": "string",
            "description": "Where the call came from (as `SessionListed.remote_address`)."
          },
          "request_id": {
            "type": "string",
            "description": "The call's `X-Request-Id`."
          }
        }
      },
      "SessionList": {
        "type": "object",
        "additionalProperties": false,
        "description": "The user's sessions, the newest first.",
        "required": [
          "data",
          "next_cursor"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SessionListed"
            }
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Where the next page begins; null on the last."
          }
        }
      },
      "SessionListed": {
        "type": "object",
        "additionalProperties": false,
        "description": "A session of the user's that has not ended or run out.",
        "required": [
          "object",
          "id",
          "created_at",
          "last_used_at",
          "expires_at",
          "remote_address",
          "user_agent",
          "current"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "session"
          },
          "id": {
            "type": "string",
            "pattern": "^ses-[0-9a-hjkmnp-tv-z]{16}$"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "last_used_at": {
            "type": "string",
            "format": "date-time",
            "description": "When a call last came with it (written every half minute, and known in between)."
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "description": "Thirty days after it was last used."
          },
          "remote_address": {
            "type": "string",
            "description": "Where the sign-in came from: the peer of its connection, or what `X-Forwarded-For` says where the peer is a proxy the control plane is told of (`--console-proxies`)."
          },
          "user_agent": {
            "type": "string",
            "description": "The sign-in's `User-Agent`: its first 200 characters, without control characters."
          },
          "current": {
            "type": "boolean",
            "description": "Whether it is the session of this call."
          }
        }
      },
      "AccountClosure": {
        "type": "object",
        "additionalProperties": false,
        "description": "An account its owner closed, and how far what it stored is deleted.",
        "required": [
          "object",
          "account_id",
          "closed_at",
          "state",
          "done_at"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "account_closure"
          },
          "account_id": {
            "type": "string",
            "pattern": "^acct-[0-9a-hjkmnp-tv-z]{16}$"
          },
          "closed_at": {
            "type": "string",
            "format": "date-time"
          },
          "state": {
            "type": "string",
            "x-extensible-enum": [
              "deleting",
              "done"
            ],
            "description": "`deleting`: what it stored is being deleted; `done`: all of it is."
          },
          "done_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When all it stored was deleted; null until then."
          }
        }
      },
      "AccountProjectList": {
        "type": "object",
        "additionalProperties": false,
        "description": "The account's projects, the newest first.",
        "required": [
          "data",
          "next_cursor"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AccountProject"
            }
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Where the next page begins; null on the last."
          }
        }
      },
      "AccountProject": {
        "type": "object",
        "additionalProperties": false,
        "description": "A project of the account.",
        "required": [
          "object",
          "id",
          "name",
          "created_at",
          "spend_limit_usd"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "project"
          },
          "id": {
            "type": "string",
            "pattern": "^prj-[0-9a-hjkmnp-tv-z]{16}$"
          },
          "name": {
            "type": "string"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "spend_limit_usd": {
            "type": [
              "number",
              "null"
            ],
            "description": "The project's spend limit (`/v1/limits`); null: none."
          }
        }
      },
      "AccountProjectCreate": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "name"
        ],
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100,
            "description": "1 to 100 characters, none of them a control character."
          }
        }
      },
      "MemberList": {
        "type": "object",
        "additionalProperties": false,
        "description": "The account's members, the newest first.",
        "required": [
          "data",
          "next_cursor"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Member"
            }
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Where the next page begins; null on the last."
          }
        }
      },
      "MemberInvite": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "email",
          "role"
        ],
        "properties": {
          "email": {
            "type": "string"
          },
          "role": {
            "type": "string",
            "enum": [
              "owner",
              "admin",
              "member"
            ]
          }
        }
      },
      "Invitation": {
        "type": "object",
        "additionalProperties": false,
        "description": "An invitation of an address into an account, with a role.",
        "required": [
          "object",
          "id",
          "account_id",
          "email",
          "role",
          "invited_by",
          "created_at",
          "expires_at"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "invitation"
          },
          "id": {
            "type": "string",
            "pattern": "^inv-[0-9a-hjkmnp-tv-z]{16}$"
          },
          "account_id": {
            "type": "string",
            "pattern": "^acct-[0-9a-hjkmnp-tv-z]{16}$"
          },
          "email": {
            "type": "string",
            "description": "The address invited, in lower case."
          },
          "role": {
            "type": "string",
            "x-extensible-enum": [
              "owner",
              "admin",
              "member"
            ]
          },
          "invited_by": {
            "type": [
              "string",
              "null"
            ],
            "description": "The user who invited (its id); null: the platform's operator (`POST /internal/accounts/{id}/owners`)."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "description": "Seven days after it was made: its link opens nothing after it."
          }
        }
      },
      "MemberUpdate": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "role"
        ],
        "properties": {
          "role": {
            "type": "string",
            "enum": [
              "owner",
              "admin",
              "member"
            ]
          }
        }
      },
      "InvitationList": {
        "type": "object",
        "additionalProperties": false,
        "description": "The account's invitations that may still be taken, the newest first.",
        "required": [
          "data",
          "next_cursor"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Invitation"
            }
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Where the next page begins; null on the last."
          }
        }
      },
      "DomainCreate": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "domain"
        ],
        "description": "`sandbox` and `port`, or `app`.",
        "properties": {
          "domain": {
            "type": "string",
            "description": "The name, such as `app.example.com`.",
            "maxLength": 254
          },
          "sandbox": {
            "type": "string",
            "description": "The sandbox's id or name. With `port`."
          },
          "port": {
            "type": "integer",
            "minimum": 1,
            "maximum": 65535
          },
          "app": {
            "type": "string",
            "description": "The app's id or name: the domain leads to the app, as its default domain does. Without `sandbox` and `port`."
          },
          "proxied": {
            "type": "boolean",
            "description": "Whether the name is served through the platform's proxy (see `Domain.proxied`). Not given: through it where the platform has one. `true` where it has none is 400 `invalid_field`."
          }
        }
      },
      "Domain": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id",
          "object",
          "domain",
          "sandbox_id",
          "port",
          "app_id",
          "status",
          "proxied",
          "proxy",
          "verification",
          "certificate",
          "created_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^dom-[0-9a-hjkmnp-tv-z]{16}$"
          },
          "object": {
            "type": "string",
            "enum": [
              "domain"
            ]
          },
          "domain": {
            "type": "string"
          },
          "sandbox_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "The sandbox the name leads to. It stays when the sandbox is deleted (`detached`). `null` for a domain that leads to an app."
          },
          "port": {
            "type": [
              "integer",
              "null"
            ],
            "description": "`null` for a domain that leads to an app."
          },
          "app_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "The app the name leads to. It stays when the app is deleted (`detached`). `null` for a domain that leads to a sandbox's port."
          },
          "status": {
            "type": "string",
            "x-extensible-enum": [
              "pending_verification",
              "pending_certificate",
              "active",
              "detached"
            ],
            "description": "`pending_verification`: the proof is not in DNS (or was, and another registration has the name now: `verification.status` is `lost`). `pending_certificate`: the name is this registration's and has no certificate yet; `certificate.error` says what is in the way. `active`: served. `detached`: the sandbox or the app it was for is gone. For a name served through the proxy, `active` once the proxy serves it with its certificate (`proxy.status` is `active`), and `pending_certificate` until then."
          },
          "proxied": {
            "type": "boolean",
            "description": "Whether the name is served through the platform's proxy (Cloudflare for SaaS: control/README.md, 入口挡在 Cloudflare 后面): its CNAME leads to `<token>.<zone>`, the proxy holds its certificate and takes its requests first, with the zone's protection and limits (a body of 100 MB at the most; no more than 125 s between two reads of the answer). False: the name reaches the edges directly, by `<token>.<direct zone>` or the edges' addresses, with the edge's own certificate and limits. A registration is proxied unless it says otherwise where the platform has a proxy, and never where it has none."
          },
          "proxy": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/DomainProxy"
              },
              {
                "type": "null"
              }
            ],
            "description": "The name at the proxy, for a registration that is proxied: null for one that is not."
          },
          "verification": {
            "$ref": "#/components/schemas/Verification"
          },
          "certificate": {
            "$ref": "#/components/schemas/Certificate"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "DomainProxy": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "status",
          "hostname_status",
          "certificate_status",
          "records",
          "http",
          "errors",
          "checked_at"
        ],
        "description": "A proxied name at the proxy: what the platform made there for it (a custom hostname of Cloudflare for SaaS), and what the proxy says of it.",
        "properties": {
          "status": {
            "type": "string",
            "x-extensible-enum": [
              "waiting",
              "full",
              "pending",
              "failed",
              "active"
            ],
            "description": "`waiting`: the registration does not hold its name yet, and nothing is made at the proxy until it does. `full`: the platform's proxy serves as many names as it is set up to, and this one waits for room. `pending`: the proxy validates the name and issues its certificate, which it does by itself once the name points here, or earlier where the `records` are added. `failed`: the proxy gave up, blocked the name, or refused to take it (`errors` has its words). `active`: the proxy serves the name."
          },
          "hostname_status": {
            "type": [
              "string",
              "null"
            ],
            "description": "The proxy's word for the name's validation, as it says it (`pending`, `active`, `moved`, `blocked`, ...)."
          },
          "certificate_status": {
            "type": [
              "string",
              "null"
            ],
            "description": "The proxy's word for the name's certificate (`pending_validation`, `pending_issuance`, `pending_deployment`, `active`, `validation_timed_out`, ...)."
          },
          "records": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DomainProxyRecord"
            },
            "description": "Records the customer may add to have the name validated and its certificate issued before the name points here (moving a site that is served elsewhere now). Empty once the proxy has no need of them."
          },
          "http": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DomainProxyHttp"
            },
            "description": "What the customer may answer instead, at the name where it points now: a path and its body."
          },
          "errors": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "What the proxy says is in the way, in its own words."
          },
          "checked_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the platform last heard of the name from the proxy."
          }
        }
      },
      "DomainProxyRecord": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "type",
          "name",
          "value",
          "for"
        ],
        "properties": {
          "type": {
            "type": "string",
            "x-extensible-enum": [
              "TXT",
              "CNAME"
            ]
          },
          "name": {
            "type": "string"
          },
          "value": {
            "type": "string"
          },
          "for": {
            "type": "string",
            "x-extensible-enum": [
              "hostname",
              "certificate"
            ],
            "description": "What it proves: the name, or the name for its certificate."
          }
        }
      },
      "DomainProxyHttp": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "url",
          "body",
          "for"
        ],
        "properties": {
          "url": {
            "type": "string"
          },
          "body": {
            "type": "string"
          },
          "for": {
            "type": "string",
            "x-extensible-enum": [
              "hostname",
              "certificate"
            ]
          }
        }
      },
      "Verification": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "status",
          "records",
          "checked_at",
          "verified_at",
          "error"
        ],
        "properties": {
          "status": {
            "type": "string",
            "x-extensible-enum": [
              "pending",
              "verified",
              "lost"
            ]
          },
          "records": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "cname",
              "txt",
              "addresses"
            ],
            "description": "What proves control of the name: either record is enough. They stay the same for as long as the project and the name do.",
            "properties": {
              "cname": {
                "oneOf": [
                  {
                    "$ref": "#/components/schemas/DnsRecord"
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "Proof and route at once. Null when the platform has no zone for it."
              },
              "txt": {
                "oneOf": [
                  {
                    "$ref": "#/components/schemas/DnsRecord"
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "Proof only: the name has to reach the edge some other way. Null for a name too long to put the record's label in front of."
              },
              "addresses": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "The edge's addresses, for A and AAAA records. Empty when the control plane is not told them."
              }
            }
          },
          "checked_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When DNS was last asked."
          },
          "verified_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "error": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/Problem"
              },
              {
                "type": "null"
              }
            ],
            "description": "`proof_not_found`, `other_proof_present` (DNS shows this registration's proof and another one: the name is given to this registration when only its own is there), `proof_being_confirmed` (this registration's proof was found alone; the name is being served for another registration, and is given to this one when the proof is found alone again at least ten minutes later), `dns_unavailable`, `gave_up` (seven days without the record), `lost`. What is said is what DNS and the edge show to anybody, never anything of another registration."
          }
        }
      },
      "DnsRecord": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "type",
          "name",
          "value"
        ],
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "CNAME",
              "TXT"
            ]
          },
          "name": {
            "type": "string"
          },
          "value": {
            "type": "string"
          }
        }
      },
      "Problem": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "code",
          "message"
        ],
        "properties": {
          "code": {
            "type": "string"
          },
          "message": {
            "type": "string"
          }
        }
      },
      "Certificate": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "status",
          "not_before",
          "not_after",
          "checked_at",
          "error"
        ],
        "properties": {
          "status": {
            "type": "string",
            "x-extensible-enum": [
              "none",
              "valid"
            ],
            "description": "What the edge showed for the name when the control plane last made a TLS handshake with it."
          },
          "not_before": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "not_after": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "checked_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "error": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/Problem"
              },
              {
                "type": "null"
              }
            ],
            "description": "Why there is no certificate; or, beside a certificate that is valid (`status` is `valid`), why it will not be renewed as things are: `proof_gone`, `not_reaching_edge`, `not_resolving` or `dns_unavailable` (the project is told of that by the event `domain.renewal_stopped`, and by `domain.renewal_resumed` when it is over). `proof_gone` (the record that proved the registration is no longer in DNS: no certificate is asked for or renewed until it is there again), `not_resolving`, `not_reaching_edge` (a request to the name on port 80 does not arrive at the edge by every address the name has, A and AAAA, or arrives by way of another server: a name behind a proxy or CDN gets no certificate), `dns_unavailable` (DNS gives no answer about the name: beside a valid certificate after three looks in a row), `waiting_for_turn` (certificates are asked for at a limited rate), `too_many_removed_domains` (the account has removed as many names as it may have waiting at the edge, which still holds their certificates: no new certificate until the edge has let go of some), `not_issued` (the CA has not issued one), `edge_unreachable`, `no_edge`. For a name served through the proxy: `proxy_pending` (the proxy validates the name and issues its certificate), `proxy_failed` (it gave up: `proxy.errors` has its words), `proxy_blocked`, `proxy_moved` (the name no longer points here), `proxy_full` (the platform's proxy serves as many names as it is set up to, and this one waits), `proxy_refused` (the proxy refused to take the name)."
          }
        }
      },
      "DomainList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "data",
          "next_cursor"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Domain"
            }
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "DomainUpdate": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "sandbox": {
            "type": "string",
            "description": "The sandbox's id or name."
          },
          "port": {
            "type": "integer",
            "minimum": 1,
            "maximum": 65535
          },
          "app": {
            "type": "string",
            "description": "The app's id or name. Without `sandbox` and `port`."
          },
          "proxied": {
            "type": "boolean",
            "description": "Whether the name is served through the platform's proxy (see `Domain.proxied`). Not given: through it where the platform has one. `true` where it has none is 400 `invalid_field`. Changing it takes the name to the other way: its certificate is the other one's, and is looked for anew."
          }
        }
      },
      "Account": {
        "type": "object",
        "additionalProperties": false,
        "description": "The account of the key's project, as the control plane shows it: the node's `Account` (`sandboxd/openapi/openapi.json`), with which account and project it is, where its notices go and what it owes, where it pays from, and with the account's own traffic.",
        "required": [
          "object",
          "id",
          "name",
          "project",
          "email",
          "email_verified",
          "email_undeliverable",
          "language",
          "egress_overage",
          "egress_overage_cap_bytes",
          "egress",
          "debt",
          "payment_hold",
          "caps",
          "keeps",
          "logs",
          "billing_address",
          "tax_id",
          "tax"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "account"
          },
          "id": {
            "type": "string",
            "pattern": "^acct-[0-9a-hjkmnp-tv-z]{16}$"
          },
          "logs": {
            "$ref": "#/components/schemas/AccountLogs",
            "description": "The limits its apps' logs are held to."
          },
          "keeps": {
            "$ref": "#/components/schemas/AccountKeeps"
          },
          "caps": {
            "$ref": "#/components/schemas/AccountCaps",
            "description": "What the account may have at once and what it has of each now: the caps of `CapLimits` (the `caps` of `GET /v1/billing`), each with `used` beside `limit` and `source` (control/README.md, 同时能有多少)."
          },
          "name": {
            "type": "string"
          },
          "project": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "name"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              }
            },
            "description": "The project the key is of."
          },
          "email": {
            "type": [
              "string",
              "null"
            ],
            "description": "Where the account's notices are sent: what it owes, what is to be deleted for it. Null: nowhere yet, and they wait until it has an address. Only a verified one is written to (`email_verified`)."
          },
          "email_verified": {
            "type": "boolean",
            "description": "Whether `email` is verified: only a verified address is written to. An address the account sets is verified at once where it is the verified one the account has, or the verified address of a user who is a member of the account; otherwise a link that verifies it is mailed to it (`verify_account_email`, a week), and its notices wait until it is opened (`POST /v1/auth/account-email/verify`), as an account's with no address do, and so does what is counted from them (the days of a debt). An operator's address is verified by its word; a user's verification verifies the address of the accounts whose notices go to it. False with no address."
          },
          "email_undeliverable": {
            "oneOf": [
              {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "why",
                  "since",
                  "detail"
                ],
                "properties": {
                  "why": {
                    "type": "string",
                    "enum": [
                      "bounced",
                      "complained"
                    ]
                  },
                  "since": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "detail": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "description": "What the sender said."
                  }
                }
              },
              {
                "type": "null"
              }
            ],
            "description": "Mail to `email` bounced, or was complained of, as the sender said after it took it: no notice goes to it (they wait, as an account's with no address do), until the account sets an address again (the same one too). Null while mail goes to it."
          },
          "language": {
            "$ref": "#/components/schemas/AccountLanguage"
          },
          "egress_overage": {
            "type": "string",
            "x-extensible-enum": [
              "stop",
              "pay"
            ],
            "description": "What happens at the allowance, as things stand: `pay` for an account on Paid that holds a balance (what is past what its period includes is taken from it), `stop` for any other: one on Free, whose traffic stops at the day's quota whatever it holds, and one that holds nothing."
          },
          "egress_overage_cap_bytes": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int64",
            "minimum": 0,
            "description": "The most bytes the account sends in a calendar month; null: no cap."
          },
          "egress": {
            "$ref": "#/components/schemas/AccountEgress"
          },
          "debt": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/Debt"
              },
              {
                "type": "null"
              }
            ],
            "description": "What the account owes, the same as the `debt` of `GET /v1/billing`; null for an account that owes nothing."
          },
          "payment_hold": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/AccountHoldShown"
              },
              {
                "type": "null"
              }
            ],
            "description": "That the account is held for its payments, since when, and to write to support; null for one that is not. Nothing of why: that is the operator's (`GET /internal/accounts/{id}`, `payment_hold`)."
          },
          "billing_address": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/BillingAddress"
              },
              {
                "type": "null"
              }
            ],
            "description": "Where the account pays from, which its payments are taxed by; null: it gave none yet, and its payment page asks for it."
          },
          "tax_id": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/TaxId"
              },
              {
                "type": "null"
              }
            ],
            "description": "A business's tax id, with what its verification came to; null: none."
          },
          "tax": {
            "$ref": "#/components/schemas/TaxStanding"
          }
        }
      },
      "AccountLogs": {
        "type": "object",
        "additionalProperties": false,
        "description": "The limits an app's log is held to: each the strictest that a machine that is there runs with (its start parameters, `app_logs` of its `GET /internal/node` in `sandboxd/openapi/openapi.json`), or what a machine runs with when it is told none where none says; and how many may follow one app's log at a time.",
        "required": [
          "line_bytes",
          "lines_per_second",
          "instance_bytes",
          "deleted_instance_bytes",
          "deleted_instance_days",
          "app_bytes_per_node",
          "followers"
        ],
        "properties": {
          "line_bytes": {
            "type": "integer",
            "minimum": 1,
            "description": "The longest line of output, in bytes: what a line has beyond it is cut off, and the line says how much (`cut_bytes`). 16384 unless the machines say otherwise."
          },
          "lines_per_second": {
            "type": "integer",
            "minimum": 1,
            "description": "How many lines of one instance's output are kept for any one second: the lines beyond are dropped, and a line of the log says how many (`output.dropped`). 500."
          },
          "instance_bytes": {
            "type": "integer",
            "format": "int64",
            "minimum": 1,
            "description": "What a machine keeps of an instance that is there: at this many bytes of its file the older half goes. 4 MiB."
          },
          "deleted_instance_bytes": {
            "type": "integer",
            "format": "int64",
            "minimum": 0,
            "description": "What a machine keeps of an instance that was deleted: the last this many bytes of its file. 1 MiB."
          },
          "deleted_instance_days": {
            "type": "number",
            "minimum": 0,
            "description": "For how many days that is kept, from when the instance was deleted. 7."
          },
          "app_bytes_per_node": {
            "type": "integer",
            "format": "int64",
            "minimum": 1,
            "description": "What a machine keeps of one app altogether: beyond it what is left of the instances deleted longest ago goes first. 1 GiB."
          },
          "followers": {
            "type": "integer",
            "minimum": 1,
            "description": "How many readers may follow the logs of the account's apps at a time, every project's apps together (`follow=true` of `GET /v1/apps/{app}/logs`): its plan's figure (`log_followers=` of the plans' flags; 10 on Free and 100 on Paid as the control plane is started unless told otherwise). The one beyond is refused, 429 `too_many_followers`."
          }
        }
      },
      "AccountKeeps": {
        "type": "object",
        "additionalProperties": false,
        "description": "What the key's project keeps of volumes and Drives (a Drive is counted among its volumes; a database's own Drive is not, and a keyed instance's is in what they hold together and not in their number), by the numbers a create is held to (`POST /v1/volumes`, `POST /v1/drives`: 403 `volume_limit_reached`, `drive_limit_reached`), with what the project has beside each: the plan's numbers, or an operator's for the account (raised on request: `POST /v1/limits/requests`, `volumes` and `volume_bytes`). On Free 3, of at most 10 GiB each, 15 GiB together, and 5 GiB where a create gives no size; on a paid plan 500 (a soft limit), of at most 1 TiB each, no total, and a node's own 64 GiB where none is given.",
        "required": [
          "volumes",
          "volume_bytes",
          "volumes_bytes",
          "unsized_bytes"
        ],
        "properties": {
          "volumes": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "limit",
              "used",
              "source"
            ],
            "properties": {
              "limit": {
                "type": "integer",
                "minimum": 0,
                "description": "How many volumes and Drives the project may have."
              },
              "used": {
                "type": "integer",
                "minimum": 0,
                "description": "How many it has now."
              },
              "source": {
                "type": "string",
                "x-extensible-enum": [
                  "plan",
                  "operator"
                ],
                "description": "Whose number `limit` is: the plan's, or an operator's for the account."
              }
            }
          },
          "volume_bytes": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "limit",
              "source"
            ],
            "properties": {
              "limit": {
                "type": "integer",
                "format": "int64",
                "minimum": 0,
                "description": "How large one may be, in bytes."
              },
              "source": {
                "type": "string",
                "x-extensible-enum": [
                  "plan",
                  "operator"
                ],
                "description": "Whose number `limit` is: the plan's, or an operator's for the account."
              }
            }
          },
          "volumes_bytes": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "limit",
              "used"
            ],
            "properties": {
              "limit": {
                "type": [
                  "integer",
                  "null"
                ],
                "format": "int64",
                "minimum": 0,
                "description": "How large all of them may be together, in bytes; null: no limit (Paid, which bills what is stored)."
              },
              "used": {
                "type": "integer",
                "format": "int64",
                "minimum": 0,
                "description": "How large the project's are together now."
              }
            }
          },
          "unsized_bytes": {
            "type": "integer",
            "format": "int64",
            "minimum": 0,
            "description": "The size a volume or a Drive made with no `size_bytes` is given: the largest one may be, or where the plan holds them to a total, an even share of it (5 GiB on Free: 15 GiB for 3), or a node's own (64 GiB) where that is less."
          }
        }
      },
      "AccountCaps": {
        "type": "object",
        "additionalProperties": false,
        "description": "What the account may have at once, and what it has of each now: the caps of `CapLimits`, each with `used`.",
        "required": [
          "instances",
          "vcpus",
          "memory_bytes",
          "sleeping",
          "app_instances",
          "creates_per_minute",
          "creates_per_hour_per_project",
          "app_creates_per_hour"
        ],
        "properties": {
          "instances": {
            "$ref": "#/components/schemas/CapUsed",
            "description": "Sandboxes and app instances awake at once."
          },
          "vcpus": {
            "$ref": "#/components/schemas/CapUsed",
            "description": "Their vCPUs together."
          },
          "memory_bytes": {
            "$ref": "#/components/schemas/CapUsed",
            "description": "Their memory together, in bytes."
          },
          "sleeping": {
            "$ref": "#/components/schemas/CapUsed",
            "description": "Sandboxes kept that do not run."
          },
          "app_instances": {
            "$ref": "#/components/schemas/AppInstancesCapUsed"
          },
          "creates_per_minute": {
            "$ref": "#/components/schemas/CapUsed",
            "description": "Sandboxes made in the last minute."
          },
          "creates_per_hour_per_project": {
            "$ref": "#/components/schemas/CapUsed",
            "description": "Sandboxes one project made in the last hour: `used` is the most of any project of the account."
          },
          "app_creates_per_hour": {
            "$ref": "#/components/schemas/CapUsed",
            "description": "Apps and versions of apps made in the last hour."
          }
        }
      },
      "CapUsed": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "limit",
          "source",
          "used"
        ],
        "properties": {
          "limit": {
            "type": "integer",
            "minimum": 0,
            "description": "What the account may have."
          },
          "source": {
            "type": "string",
            "x-extensible-enum": [
              "plan",
              "operator"
            ],
            "description": "Whose number `limit` is: its plan's, or one an operator set for the account."
          },
          "used": {
            "type": "integer",
            "minimum": 0,
            "description": "What it has now, as the last look found it and the calls since have changed it."
          }
        }
      },
      "AppInstancesCapUsed": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "limit",
          "source",
          "used",
          "default"
        ],
        "properties": {
          "limit": {
            "type": "integer",
            "minimum": 0,
            "description": "What the account may have."
          },
          "source": {
            "type": "string",
            "x-extensible-enum": [
              "plan",
              "operator"
            ],
            "description": "Whose number `limit` is: its plan's, or one an operator set for the account."
          },
          "used": {
            "type": "integer",
            "minimum": 0,
            "description": "The most instances any one version of one of the account's apps has now, awake or asleep, on all its nodes (what the nodes' inventories last showed)."
          },
          "default": {
            "type": "integer",
            "minimum": 0,
            "description": "What an app has when it asks for no `scale.max_instances`."
          }
        }
      },
      "AccountLanguage": {
        "type": "object",
        "additionalProperties": false,
        "description": "The language the account's notices are written in, and what says so (control/README.md, 通知的语言). The acknowledgement of a withdrawal and the confirmation of a purchase are written in it; a notice the language has no words for, in English.",
        "required": [
          "code",
          "source"
        ],
        "properties": {
          "code": {
            "type": "string",
            "x-extensible-enum": [
              "en",
              "de",
              "fr"
            ],
            "description": "ISO 639-1: one of the languages the notices are written in."
          },
          "source": {
            "type": "string",
            "x-extensible-enum": [
              "account",
              "billing_address",
              "default"
            ],
            "description": "`account`: the account chose it (`PATCH /v1/account` with `language`). `billing_address`: it chose none, and this is the language most people of its billing address's country read first. `default`: neither says (no choice, and no billing address, or one whose country's language the notices are not written in): English."
          }
        }
      },
      "AccountEgress": {
        "type": "object",
        "additionalProperties": false,
        "description": "What the account's sandboxes have sent to the internet, over all its projects and all the nodes, against the allowance: the `egress` of `GET /v1/account`, and of `GET /v1/usage` when it lists sandboxes. The node's `AccountEgress` with the stretch, the calendar month, the project, what is past the allowance comes to, and why traffic is stopped.",
        "required": [
          "period",
          "from",
          "to",
          "bytes",
          "allowance_bytes",
          "overage_bytes",
          "overage_usd",
          "regions",
          "month_bytes",
          "stopped",
          "stopped_reason",
          "prices",
          "project_bytes",
          "counted"
        ],
        "properties": {
          "period": {
            "type": "string",
            "pattern": "^[0-9]{4}-[0-9]{2}$",
            "description": "The calendar month it is, in UTC."
          },
          "from": {
            "type": "string",
            "format": "date-time",
            "description": "The stretch the allowance is for: the period of Paid the account is in, or Free's day (UTC)."
          },
          "to": {
            "type": "string",
            "format": "date-time"
          },
          "bytes": {
            "type": "integer",
            "format": "int64",
            "minimum": 0,
            "description": "Sent in the stretch."
          },
          "allowance_bytes": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int64",
            "minimum": 0,
            "description": "What the stretch includes; null: whatever is sent is included."
          },
          "overage_bytes": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int64",
            "minimum": 0,
            "description": "How far `bytes` is past the allowance; null without one."
          },
          "overage_usd": {
            "type": "number",
            "minimum": 0,
            "description": "What the bytes past the allowance come to, at the list price: on Paid the account's balance pays it; on Free nothing is priced, and traffic stops at the day's quota."
          },
          "regions": {
            "$ref": "#/components/schemas/EgressMonthRegions"
          },
          "month_bytes": {
            "type": "integer",
            "format": "int64",
            "minimum": 0,
            "description": "Sent in the calendar month `period`: the account's own cap is held against this."
          },
          "stopped": {
            "type": "boolean",
            "description": "Whether the account's traffic to the internet is stopped now."
          },
          "stopped_reason": {
            "type": [
              "string",
              "null"
            ],
            "x-extensible-enum": [
              "allowance_reached",
              "overage_cap_reached"
            ],
            "description": "`allowance_reached`: the account has sent its allowance and holds no balance to pay for more; `overage_cap_reached`: it has sent as much in the calendar month as its own cap. Null when it is not stopped."
          },
          "prices": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "egress_gb_usd",
              "egress_eu_gb_usd",
              "egress_ap_gb_usd",
              "provisional"
            ],
            "properties": {
              "egress_gb_usd": {
                "type": "number",
                "minimum": 0,
                "description": "US dollars for a gigabyte (10^9 bytes) past the allowance."
              },
              "egress_eu_gb_usd": {
                "type": "number"
              },
              "egress_ap_gb_usd": {
                "type": "number"
              },
              "provisional": {
                "type": "boolean"
              }
            }
          },
          "project_bytes": {
            "type": "integer",
            "format": "int64",
            "minimum": 0,
            "description": "What the project of the calling key sent in the calendar month; the allowance is the account's."
          },
          "counted": {
            "type": "boolean",
            "description": "Whether every node asked is counting traffic."
          }
        }
      },
      "EgressMonthRegions": {
        "type": "object",
        "additionalProperties": false,
        "description": "Of the traffic of the stretch, region by region: the region of the node it left from: `na` North America (and a node with no region), `eu` Europe, `ap` Asia-Pacific. The allowance takes the dearest region's bytes first; each region is priced and rounded on its own, and `overage_usd` is theirs added up.",
        "required": [
          "na",
          "eu",
          "ap"
        ],
        "properties": {
          "na": {
            "$ref": "#/components/schemas/EgressMonthRegion"
          },
          "eu": {
            "$ref": "#/components/schemas/EgressMonthRegion"
          },
          "ap": {
            "$ref": "#/components/schemas/EgressMonthRegion"
          }
        }
      },
      "EgressMonthRegion": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "bytes",
          "overage_bytes",
          "unit_price_usd",
          "overage_usd"
        ],
        "properties": {
          "bytes": {
            "type": "integer",
            "format": "int64",
            "minimum": 0,
            "description": "Sent from the region's nodes in the stretch."
          },
          "overage_bytes": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int64",
            "minimum": 0,
            "description": "What of them is past the allowance; null without one."
          },
          "unit_price_usd": {
            "type": "number",
            "description": "The region's price of a gigabyte."
          },
          "overage_usd": {
            "type": "number",
            "minimum": 0,
            "description": "What `overage_bytes` comes to at that price, to the nearest millionth."
          }
        }
      },
      "Debt": {
        "type": "object",
        "additionalProperties": false,
        "description": "What an account owes, as the account is shown it (`debt` of `GET /v1/billing` and of `GET /v1/account`): a debt is what is owed from `--debt-charge-least` ($0.50) on.",
        "required": [
          "object",
          "id",
          "state",
          "in_debt_since",
          "owed_usd",
          "data_deleted_at",
          "notice_days",
          "on_hold",
          "charge_at",
          "last_charge",
          "notices"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "debt"
          },
          "id": {
            "type": "string"
          },
          "state": {
            "type": "string",
            "enum": [
              "owing",
              "deleting",
              "paid",
              "deleted"
            ],
            "x-extensible-enum": true,
            "description": "`owing`: the account owes, and nothing of it can be used. `deleting`: its date is past, and what it stores is being deleted. An account is shown a debt of these two only. `paid`: it was paid, or what it owes came to less than a debt is. `deleted`: what the account stored was deleted, and what it owed was written off."
          },
          "in_debt_since": {
            "type": "string",
            "format": "date-time",
            "description": "When the account was found to owe as much as a debt is."
          },
          "owed_usd": {
            "type": "number",
            "description": "What it owes now: what its balance is below zero by. Of a debt that has ended, 0."
          },
          "data_deleted_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When what the account stores is deleted if it still owes: the latest date a notice the sender accepted has said. Null while no notice of the debt has been accepted (nothing is deleted then), and while an operator holds the account. Of a debt that ended with the deletion, when that was."
          },
          "notice_days": {
            "type": "integer",
            "description": "How many days an account that owes is given from its first notice (`--debt-days`)."
          },
          "on_hold": {
            "type": "boolean",
            "description": "An operator holds the account: nothing of it is deleted, and the count of its days stands still."
          },
          "charge_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When what it owes is charged to the account's card next: 00:00 UTC on its first billing day after the debt began, or after it was last charged (a billing day is the day of the month the account's subscription began on, or, without one, the day its first top-up was collected; the last day of a month that has no such day). A time that has come is one to charge at the next look. Null: nothing is to be charged, as the account has no card on record (`Balance.card`) or no billing day, an operator holds it, its deletion has begun, or it owes less than a debt is. What is charged is what it owes then, rounded up to the cent, and no more than the account's spend limit leaves in the calendar month (`Balance.reload_left_usd`): what that leaves owed is charged on the next billing day; where the limit leaves less than a debt is, nothing is charged that month, and the first look of the next month charges it (this time stays the one that has come until then)."
          },
          "last_charge": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/DebtCharge"
              },
              {
                "type": "null"
              }
            ],
            "description": "The last charge of what it owes to the account's card, and how it went; null before the first."
          },
          "notices": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DebtNotice"
            },
            "description": "The notices of this debt, the oldest first: the first one, and, once that is accepted, the reminders that are to come; and one of each charge of it that failed (`charge_failed`)."
          }
        }
      },
      "DebtCharge": {
        "type": "object",
        "additionalProperties": false,
        "description": "A charge of what a debt owes to the account's card, on its billing day: a top-up of it (`GET /v1/billing/charges`, `debt_id`).",
        "required": [
          "charge_id",
          "at",
          "amount_usd",
          "outcome",
          "failure"
        ],
        "properties": {
          "charge_id": {
            "type": "string"
          },
          "at": {
            "type": "string",
            "format": "date-time",
            "description": "When it was made."
          },
          "amount_usd": {
            "type": "number",
            "description": "What was charged: what was owed then, in whole cents rounded up."
          },
          "outcome": {
            "type": "string",
            "enum": [
              "to_collect",
              "collected",
              "failed",
              "refunded"
            ],
            "x-extensible-enum": true,
            "description": "`to_collect`: whoever collects has it. `collected`: its Credits paid what was owed first. `failed`: nothing was taken; the account was sent a notice of it (`charge_failed`), and the next billing day it is charged again. `refunded`: collected, and taken back by the operator."
          },
          "failure": {
            "oneOf": [
              {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "code",
                  "message"
                ],
                "properties": {
                  "code": {
                    "type": "string"
                  },
                  "message": {
                    "type": "string"
                  }
                }
              },
              {
                "type": "null"
              }
            ],
            "description": "That it failed, as a charge's `failure` says it: to the account `card_declined` or `payment_failed` and never why, to the operator (`GET /internal/debts`) why, as whoever collects says it; null unless `failed`."
          }
        }
      },
      "DebtNotice": {
        "type": "object",
        "additionalProperties": false,
        "description": "A notice of a debt, as the account is shown it.",
        "required": [
          "id",
          "object",
          "kind",
          "state",
          "due_at",
          "sent_at",
          "subject"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "object": {
            "type": "string",
            "const": "notice"
          },
          "kind": {
            "type": "string",
            "enum": [
              "in_debt",
              "reminder_7",
              "reminder_14",
              "reminder_21",
              "reminder_27",
              "reminder_29",
              "charge_failed",
              "data_deleted",
              "storage_80",
              "storage_100",
              "subscription_charge_failed",
              "reload_failed",
              "included_80",
              "included_100"
            ],
            "x-extensible-enum": true,
            "description": "`in_debt`: the first notice of a debt. `reminder_<day>`: on that day after it (the days are the control plane's, `--debt-reminder-days`). `charge_failed`: what it owes was charged to the account's card on its billing day, and the charge failed; one of each such charge. `data_deleted`: what the account stored has been deleted, and the debt is written off. `storage_80`, `storage_100`: before any debt, the storage the account's plan includes is used to four fifths, or used up, and it holds nothing. `subscription_charge_failed`: the fee of the period of its subscription that was due could not be charged; one of each such charge, the first and each one made again in the days of grace. `reload_failed`: an auto reload could not be charged; one of each. `reload_no_card`: auto reload is on and the balance below its line, and the account has no card on record to charge; once a calendar month while it is so. `included_80`, `included_100`: what its plan includes of CPU time, memory, requests or traffic is four fifths used, or all, and it holds nothing and auto reload will not top it up; once a meter, a mark and a stretch."
          },
          "state": {
            "type": "string",
            "enum": [
              "to_send",
              "accepted",
              "failed",
              "cancelled"
            ],
            "x-extensible-enum": true,
            "description": "`to_send`: not handed to the sender yet, or handed over and not answered. `accepted`: the sender took it to deliver. `failed`: the sender said that it could not; of a debt's notice another is made after a while. `cancelled`: it is sent no more (the debt was paid; a later reminder said what it would have). An account is not shown its cancelled ones."
          },
          "due_at": {
            "type": "string",
            "format": "date-time",
            "description": "From when it is handed to the sender."
          },
          "sent_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When it was handed to the sender, of one the sender accepted; null otherwise. Of a debt's first notice: the moment the debt's days are counted from."
          },
          "subject": {
            "type": [
              "string",
              "null"
            ],
            "description": "Its subject, once it has been handed over."
          }
        }
      },
      "AccountHoldShown": {
        "type": "object",
        "additionalProperties": false,
        "description": "What an account that is held for its payments is shown of it (`GET /v1/account`, `payment_hold`): that it is, since when, and to write to support.",
        "required": [
          "object",
          "since",
          "message"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "payment_hold"
          },
          "since": {
            "type": "string",
            "format": "date-time"
          },
          "message": {
            "type": "string",
            "description": "That nothing of the account can be used and nothing is charged to it until it is looked at, and to write to support."
          }
        }
      },
      "BillingAddress": {
        "type": "object",
        "additionalProperties": false,
        "description": "Where an account pays from, which its payments are taxed by (control/README.md, 税): as the account gave it (`PATCH /v1/account`), on its payment page or in its customer portal (at Stripe), or as an operator put it right.",
        "required": [
          "line1",
          "line2",
          "city",
          "state",
          "postal_code",
          "country",
          "source",
          "set_at"
        ],
        "properties": {
          "line1": {
            "type": [
              "string",
              "null"
            ]
          },
          "line2": {
            "type": [
              "string",
              "null"
            ]
          },
          "city": {
            "type": [
              "string",
              "null"
            ]
          },
          "state": {
            "type": [
              "string",
              "null"
            ],
            "description": "The state, province or region; of the US its two letters, by which the sales to each state are counted."
          },
          "postal_code": {
            "type": [
              "string",
              "null"
            ]
          },
          "country": {
            "type": "string",
            "pattern": "^[A-Z]{2}$",
            "description": "ISO 3166-1 alpha-2."
          },
          "source": {
            "type": [
              "string",
              "null"
            ],
            "x-extensible-enum": [
              "account",
              "operator",
              "payment_page",
              "stripe"
            ],
            "description": "Who gave it: the account, an operator (`PATCH /internal/accounts/{id}`), the account on a payment page, Stripe (the customer portal, as Stripe's `customer.updated` says it)."
          },
          "set_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "TaxId": {
        "type": "object",
        "additionalProperties": false,
        "description": "An account's tax id, with what its verification came to: Stripe's, which asks its authority (VIES for an EU VAT number, HMRC for a UK one, the ABR for an ABN), or what stands in for it.",
        "required": [
          "type",
          "value",
          "verification",
          "source",
          "set_at"
        ],
        "properties": {
          "type": {
            "type": "string",
            "description": "As Stripe names it: `eu_vat`, `gb_vat`, `au_abn`, ..."
          },
          "value": {
            "type": "string"
          },
          "verification": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "status",
              "verified_name",
              "verified_address",
              "checked_at"
            ],
            "properties": {
              "status": {
                "type": "string",
                "x-extensible-enum": [
                  "pending",
                  "verified",
                  "unverified",
                  "unavailable"
                ],
                "description": "`pending`: being verified (the account is a business meanwhile, and a payment taken so is held for an operator if it is then not verified); `verified`; `unverified`: its authority does not know it (the account is no business); `unavailable`: it could not be verified (no business either; its payments are flagged for an operator)."
              },
              "verified_name": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "The name the authority has for it, where it said."
              },
              "verified_address": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "checked_at": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time",
                "description": "When its verification was last heard."
              }
            }
          },
          "source": {
            "type": "string",
            "x-extensible-enum": [
              "account",
              "operator",
              "payment_page",
              "stripe"
            ],
            "description": "Who gave it: the account, an operator, the account on a payment page, Stripe (the customer portal)."
          },
          "set_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "TaxStanding": {
        "type": "object",
        "additionalProperties": false,
        "description": "How what the account buys is sold where it is: by its billing address, and nothing known without one (its payment page asks for it).",
        "required": [
          "country",
          "region",
          "sold_as",
          "business",
          "can_pay"
        ],
        "properties": {
          "country": {
            "type": [
              "string",
              "null"
            ],
            "description": "Its billing address's."
          },
          "region": {
            "type": [
              "string",
              "null"
            ],
            "description": "The region that is so: `eu-oss` (the EU), a country's two letters in lower case (`au`, `gb`), `us-<state>`."
          },
          "sold_as": {
            "type": [
              "string",
              "null"
            ],
            "x-extensible-enum": [
              "registered",
              "business_only",
              "sold"
            ],
            "description": "`registered`: the platform is registered for tax there, and the tax is added (a business with a verified tax id is reverse charged instead); `business_only`: it is not, and sells only to businesses there; `sold`: it is not, and sells to everyone there, with no tax."
          },
          "business": {
            "type": "boolean",
            "description": "Whether the account is a business: its tax id is verified, or being verified."
          },
          "can_pay": {
            "type": "boolean",
            "description": "Whether it can pay where it is: false only where only businesses are sold to and it is none, when a top-up, a payment page and a paid plan are refused (403 `tax_region_business_only`) and auto reload makes nothing."
          }
        }
      },
      "AccountUpdate": {
        "type": "object",
        "additionalProperties": false,
        "description": "What is given changes, and nothing else.",
        "properties": {
          "email": {
            "type": [
              "string",
              "null"
            ],
            "description": "Where the account's notices are sent; null takes the address away."
          },
          "language": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "en",
              "de",
              "fr",
              null
            ],
            "description": "The language the account's notices are written in; null takes the choice away, and its billing address says again."
          },
          "egress_overage": {
            "type": "string",
            "enum": [
              "stop",
              "pay"
            ],
            "description": "Not the account's to choose: see `PATCH /v1/account`."
          },
          "egress_overage_cap_bytes": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int64",
            "minimum": 0,
            "description": "The most bytes in a calendar month; null takes the cap away."
          },
          "billing_address": {
            "$ref": "#/components/schemas/BillingAddressGiven"
          },
          "tax_id": {
            "$ref": "#/components/schemas/TaxIdGiven"
          }
        }
      },
      "BillingAddressGiven": {
        "type": [
          "object",
          "null"
        ],
        "additionalProperties": false,
        "description": "A billing address: `country` and as many of the rest as there are, each a string of at most 200 characters; of the US the `state` and the ZIP code (`postal_code`, five digits or ZIP+4), of Canada the province (`state`) or the `postal_code`. Null takes it away.",
        "required": [
          "country"
        ],
        "properties": {
          "line1": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 200
          },
          "line2": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 200
          },
          "city": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 200
          },
          "state": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 200
          },
          "postal_code": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 200
          },
          "country": {
            "type": "string",
            "pattern": "^[A-Za-z]{2}$",
            "description": "ISO 3166-1 alpha-2."
          }
        }
      },
      "TaxIdGiven": {
        "type": [
          "object",
          "null"
        ],
        "additionalProperties": false,
        "description": "A tax id as Stripe names it; null takes it away.",
        "required": [
          "type",
          "value"
        ],
        "properties": {
          "type": {
            "type": "string",
            "pattern": "^[a-z]{2}_[a-z_]+$",
            "maxLength": 24,
            "description": "`eu_vat`, `gb_vat`, `au_abn`, ... (Stripe's types)."
          },
          "value": {
            "type": "string",
            "description": "As its authority writes it; spaces are taken out. An EU VAT number has its country's two letters before it (`DE123456789`; Greece's `EL`)."
          }
        }
      },
      "UsageList": {
        "type": "object",
        "description": "One page of usage rows, newest sandbox first, with totals and the account's month.",
        "required": [
          "data",
          "next_cursor",
          "totals",
          "egress"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/UsageRow"
            }
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Pass it as `cursor` to get the next page. Null on the last page."
          },
          "totals": {
            "type": "object",
            "description": "Sums over every row the filter selects, not over this page.",
            "required": [
              "cpu_seconds",
              "memory_gib_seconds",
              "egress_bytes",
              "sandboxes"
            ],
            "properties": {
              "cpu_seconds": {
                "type": "number",
                "minimum": 0
              },
              "memory_gib_seconds": {
                "type": "number",
                "minimum": 0
              },
              "egress_bytes": {
                "type": "integer",
                "format": "int64",
                "minimum": 0
              },
              "sandboxes": {
                "type": "integer",
                "minimum": 0
              }
            },
            "additionalProperties": false
          },
          "egress": {
            "$ref": "#/components/schemas/AccountEgress",
            "description": "The account's month: what the allowance is measured against, whichever sandboxes sent it."
          }
        },
        "additionalProperties": false
      },
      "UsageRow": {
        "type": "object",
        "description": "What one sandbox has used since it was created.",
        "required": [
          "sandbox_id",
          "name",
          "metadata",
          "cpu_seconds",
          "memory_gib_seconds",
          "disk_bytes",
          "egress_bytes",
          "created_at",
          "deleted_at"
        ],
        "properties": {
          "sandbox_id": {
            "type": "string",
            "pattern": "^sbx-[0-9a-hjkmnp-tv-z]{16}$"
          },
          "name": {
            "type": [
              "string",
              "null"
            ],
            "description": "The sandbox's name; for a deleted one, the name it had last."
          },
          "product": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "browsers",
              "databases",
              null
            ],
            "description": "What the sandbox is sold as, by the control plane's own record (never its metadata): `browsers` for a Browsers session (`POST /v1/browsers`), `databases` for an instance of a database's branch (`POST /v1/databases`), null for any other sandbox."
          },
          "metadata": {
            "type": "object",
            "description": "Free key-value pairs for the caller's own use; lists of sandboxes filter by them. At most 64 keys of 1 to 64 characters, values of up to 1024. Not a place for secrets: metadata are stored in clear, and every set of them a sandbox has had while it used something is kept with its usage for thirteen months, also after the sandbox is deleted (`GET /v1/usage` groups and filters by them).",
            "maxProperties": 64,
            "propertyNames": {
              "minLength": 1,
              "maxLength": 64
            },
            "additionalProperties": {
              "type": "string",
              "maxLength": 1024
            }
          },
          "cpu_seconds": {
            "type": "number",
            "minimum": 0
          },
          "memory_gib_seconds": {
            "type": "number",
            "minimum": 0,
            "description": "Memory in 128 MiB steps, integrated over time: GiB times seconds."
          },
          "disk_bytes": {
            "type": "integer",
            "format": "int64",
            "minimum": 0,
            "description": "What the root disk takes now; 0 for a deleted sandbox."
          },
          "egress_bytes": {
            "type": "integer",
            "format": "int64",
            "minimum": 0,
            "description": "Bytes it has sent to the internet. For a deleted sandbox, less its last second, which is only in the account's month."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "deleted_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Null for a sandbox that exists."
          }
        },
        "additionalProperties": false
      },
      "UsageReport": {
        "type": "object",
        "description": "What was used in a range of time, added up as the call asked: one page of groups, the totals over every group, the range and the grouping as they hold, and the prices the quantities are multiplied by.",
        "required": [
          "data",
          "next_cursor",
          "totals",
          "from",
          "to",
          "group_by",
          "bucket_seconds",
          "metadata_at",
          "metadata_approximate",
          "metadata_approximate_reasons",
          "prices",
          "plan"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/UsageGroup"
            }
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Pass it as `cursor` to get the next page. Null on the last page."
          },
          "totals": {
            "type": "object",
            "description": "Sums over every group the call selects, not over this page, and how many sandboxes they are of (an app that requests are booked to is not one of them). They are of the moment this page was made. Over a range that includes the present, usage is still being booked: a later page's totals are ahead of an earlier page's, and the groups collected over several pages add up to less than the totals of the last. Over a range that has ended, every page says the same.",
            "required": [
              "cpu_seconds",
              "memory_gib_seconds",
              "egress_bytes",
              "sandboxes",
              "requests",
              "state_gb_hours",
              "storage_gb_hours",
              "log_bytes",
              "log_gb_hours",
              "egress_eu_bytes",
              "egress_ap_bytes",
              "drive_write_bytes",
              "drive_cold_read_bytes",
              "browser_awake_seconds",
              "cache_gb_hours"
            ],
            "properties": {
              "cpu_seconds": {
                "type": "number",
                "minimum": 0
              },
              "memory_gib_seconds": {
                "type": "number",
                "minimum": 0
              },
              "egress_bytes": {
                "type": "integer",
                "format": "int64",
                "minimum": 0
              },
              "requests": {
                "type": "integer",
                "format": "int64",
                "minimum": 0,
                "description": "Through a control plane only, as in a group."
              },
              "state_gb_hours": {
                "type": "number",
                "minimum": 0,
                "description": "Through a control plane only, as in a group."
              },
              "storage_gb_hours": {
                "type": "number",
                "minimum": 0,
                "description": "Through a control plane only, as in a group."
              },
              "log_bytes": {
                "type": "integer",
                "format": "int64",
                "minimum": 0,
                "description": "Through a control plane only, as in a group."
              },
              "log_gb_hours": {
                "type": "number",
                "minimum": 0,
                "description": "Through a control plane only, as in a group."
              },
              "drive_write_bytes": {
                "type": "integer",
                "format": "int64",
                "minimum": 0,
                "description": "Through a control plane only, as in a group."
              },
              "drive_cold_read_bytes": {
                "type": "integer",
                "format": "int64",
                "minimum": 0,
                "description": "Through a control plane only, as in a group."
              },
              "browser_awake_seconds": {
                "type": "number",
                "minimum": 0,
                "description": "Through a control plane only, as in a group."
              },
              "cache_gb_hours": {
                "type": "number",
                "minimum": 0,
                "description": "Through a control plane only, as in a group."
              },
              "egress_eu_bytes": {
                "type": "integer",
                "format": "int64",
                "minimum": 0,
                "description": "Through a control plane only, as in a group: of `egress_bytes`, what left from a node in Europe."
              },
              "egress_ap_bytes": {
                "type": "integer",
                "format": "int64",
                "minimum": 0,
                "description": "Through a control plane only, as in a group: of `egress_bytes`, what left from a node in Asia-Pacific."
              },
              "sandboxes": {
                "type": "integer",
                "minimum": 0,
                "description": "How many sandboxes the groups are of. Volumes and snapshots are not counted here."
              }
            },
            "additionalProperties": false
          },
          "from": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "The beginning of the range as it holds: the first hour that begins at or after the `from` asked for. Null: none was asked for."
          },
          "to": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "The end of the range as it holds: the first hour that begins at or after the `to` asked for; hours before it are in the range. Null: none was asked for."
          },
          "group_by": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "What the groups are of, in the order of their keys: `hour` or `day`, `sandbox`, then each `metadata.<key>`. Through a control plane `volume`, `snapshot` and `kind` come after `sandbox`."
          },
          "bucket_seconds": {
            "type": "integer",
            "minimum": 1,
            "description": "The grain of the books: 3600, an hour. (A node started for a test with `--usage-bucket-seconds` has a shorter one.)"
          },
          "metadata_at": {
            "type": "string",
            "x-extensible-enum": [
              "time_of_use"
            ],
            "description": "Which metadata the groups and the filters go by. `time_of_use`: the ones a sandbox had while it used what a row holds."
          },
          "metadata_approximate": {
            "type": "boolean",
            "description": "True when something in the range (of the sandbox asked for, whatever the filters by metadata) may be booked under metadata other than the ones it was used under; `metadata_approximate_reasons` says why. The quantities are exact in every case; which group they are in is not, and a filter by metadata may leave some of them out."
          },
          "metadata_approximate_reasons": {
            "type": "array",
            "items": {
              "type": "string",
              "x-extensible-enum": [
                "changed_often",
                "sets_in_an_hour",
                "values_of_a_key",
                "keys_in_a_month",
                "set_too_large",
                "sets_in_a_month",
                "sets_in_all",
                "booked_on_arrival",
                "unspecified"
              ]
            },
            "description": "Why, when `metadata_approximate` is true: every reason that holds of something in the range, each once; empty when it is false. `changed_often`: a sandbox's metadata changed more than 60 times in one hour. The first 60 changes each end a row of the books; after that the hour's row goes on under the newest metadata. The others are a control plane's, which keeps the books of many nodes in one store. `sets_in_an_hour`: one sandbox has rows under at most 61 sets of metadata in an hour, and what it used under a further set is booked under no metadata. `values_of_a_key`: one metadata key had more distinct values in a calendar month than the books take in for a project (1,000). A value past that takes its key out of the set, and the use is booked under the rest of the set: a sandbox tagged with a team and the id of its job is booked under its team. Asked by that key, such use is under no value; asked by any other key, it is where it belongs. `keys_in_a_month`: the month's new sets already use 256 distinct key names; a further key is removed from a new set, preserving the other keys and all quantities. Existing sets remain exact. `set_too_large`: a set of metadata whose JSON text is longer than 4,096 bytes is not kept, and what is used under it is booked under no metadata. `sets_in_a_month`: the project had more new sets of metadata in a calendar month than the books take in (10,000); what is used under a further new set is booked under no metadata until the month turns. A set the books already have does not count again, and takes no place among a key's values either. `sets_in_all`: the project keeps as many sets of metadata as the books keep for one in all (130,000: a set is kept for thirteen months after its last use); what is used under a further new set is booked under no metadata until sets the project no longer uses have gone. `booked_on_arrival`: the sandbox's node gave a reading of the use but no hour and no metadata for it (a node that was cut off before it wrote them down, or one of an earlier version): it is booked in the hour the reading arrived in, under the metadata the sandbox had before or after. `unspecified`: the hour was marked by a version that did not keep the reason."
          },
          "prices": {
            "$ref": "#/components/schemas/Prices",
            "description": "What the quantities are multiplied by: the prices of the account's month, one a meter (and of traffic one a region)."
          },
          "plan": {
            "type": [
              "string",
              "null"
            ],
            "x-extensible-enum": [
              "free",
              "paid"
            ],
            "description": "The plan the project's account is on now (`Billing.plan`)."
          }
        },
        "additionalProperties": false
      },
      "UsageGroup": {
        "type": "object",
        "description": "What was used by one group of a report. Which of `start`, `sandbox_id` with `name`, and `metadata` it has is what the report is grouped by; the three quantities are always there. Through a control plane a group has the quantities only it counts (`requests`, `state_gb_hours`, `storage_gb_hours`, `log_bytes`, `log_gb_hours`, `egress_eu_bytes`, `egress_ap_bytes`, `drive_write_bytes`, `drive_cold_read_bytes`, `browser_awake_seconds`, `cache_gb_hours`), and may be of a volume, of a snapshot or of a kind of thing (`volume_id`, `snapshot_id`, `kind`).",
        "required": [
          "cpu_seconds",
          "memory_gib_seconds",
          "egress_bytes",
          "requests",
          "state_gb_hours",
          "storage_gb_hours",
          "log_bytes",
          "log_gb_hours",
          "egress_eu_bytes",
          "egress_ap_bytes",
          "drive_write_bytes",
          "drive_cold_read_bytes",
          "browser_awake_seconds",
          "cache_gb_hours"
        ],
        "properties": {
          "start": {
            "type": "string",
            "format": "date-time",
            "description": "With `hour` or `day`: when the hour or the day (UTC) of this group begins."
          },
          "sandbox_id": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^sbx-[0-9a-hjkmnp-tv-z]{16}$",
            "description": "With `sandbox`. Through a control plane, null in the one group of what no one sandbox used: what volumes and snapshots kept (and, where the report is also by volume or by snapshot, in every group of one of those), what the backups of volumes kept, and the requests that apps took, which are booked to the app and to none of its instances."
          },
          "volume_id": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^(vol|ibk)-[0-9a-hjkmnp-tv-z]{16}$",
            "description": "Through a control plane only, with `volume`: the volume this group is of, or the bucket of an installation of a Managed Application (`ibk-`, billed as volume data). Null in a group of what no volume kept."
          },
          "drive_id": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^vol-[0-9a-hjkmnp-tv-z]{16}$",
            "description": "Through a control plane only, with `drive` (or `drive_id`, `kind=drive`): the Drive this group is of. Null in a group of what no Drive kept."
          },
          "snapshot_id": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^snap-[0-9a-hjkmnp-tv-z]{16}$",
            "description": "Through a control plane only, with `snapshot`: the snapshot this group is of. Null in a group of what no snapshot kept."
          },
          "backup_id": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^bak-[0-9a-z]+$",
            "description": "Through a control plane only, with `backup` (or `backup_id`): the backup of a volume this group is of. Null in a group of what no backup kept. Its `name` is null: a backup has none."
          },
          "kind": {
            "type": "string",
            "x-extensible-enum": [
              "sandbox",
              "volume",
              "drive",
              "snapshot",
              "log_export",
              "backup",
              "bucket"
            ],
            "description": "Through a control plane only, with `kind` (`drive` in place of `volume` where the question says Drives): the kind of thing this group is of. `log_export`: what the export of the apps' logs sent their destinations, which is traffic to the internet (`egress_bytes`) and nothing else. `backup`: what the backups of volumes kept in the object store, on the meter of state (`state_gb_hours`) and nothing else. `browser_profiles`: what the project's Browsers' profiles keep, as volume data (`storage_gb_hours`), and nothing else. `volume`: what volumes kept (`storage_gb_hours`), and what was written to them and read of them cold (`drive_write_bytes`, `drive_cold_read_bytes`)."
          },
          "name": {
            "type": [
              "string",
              "null"
            ],
            "description": "With `sandbox`: what the sandbox is called now; for a deleted one, what it was called last. Through a control plane, with `volume` or `snapshot`, the same of the volume or the snapshot."
          },
          "product": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "browsers",
              "databases",
              null
            ],
            "description": "What the sandbox is sold as, by the control plane's own record (never its metadata): `browsers` for a Browsers session (`POST /v1/browsers`), `databases` for an instance of a database's branch (`POST /v1/databases`), null for any other sandbox. Through a control plane only, with `sandbox`."
          },
          "database_id": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^db-[0-9a-hjkmnp-tv-z]{16}$",
            "description": "Through a control plane only, with `sandbox` or `volume` (`drive`): the database the thing of this group is of (slice DB7): an instance of one of its branches, or a branch's Drive, by the databases' own records (never metadata). Null for any other thing, and in a group of no thing. With `database` or `branch`: the database of this group, null in the group of what no database's thing used."
          },
          "branch_id": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^br-[0-9a-hjkmnp-tv-z]{16}$",
            "description": "With `database_id`: the branch of the database the thing is of (the root's, `main`, or one of its own). Null where `database_id` is. With `branch`: the branch of this group."
          },
          "metadata": {
            "type": "object",
            "description": "With `metadata.<key>`: each key grouped by, with the value the sandboxes of this group had for it while they used this. Null: they had no such key.",
            "additionalProperties": {
              "type": [
                "string",
                "null"
              ]
            }
          },
          "cpu_seconds": {
            "type": "number",
            "minimum": 0,
            "description": "A whole number of microseconds, written in seconds."
          },
          "memory_gib_seconds": {
            "type": "number",
            "minimum": 0,
            "description": "Memory in 128 MiB steps, integrated over time: a whole number of steps times milliseconds, written as GiB times seconds (a multiple of 0.000125)."
          },
          "egress_bytes": {
            "type": "integer",
            "format": "int64",
            "minimum": 0,
            "description": "Bytes sent to the internet."
          },
          "requests": {
            "type": "integer",
            "format": "int64",
            "minimum": 0,
            "description": "Through a control plane only (a node does not count them): requests into the sandboxes through the platform's ingress that a node took, an HTTP request, a WebSocket and an SSH session each one; what the platform refused itself is none. An app's requests are booked to the app itself, under the labels its instances carry and the region of the edge they came by: grouped by `metadata.app` or `metadata.region` they are with what the instances used, and grouped by sandbox they are in the group of no sandbox. control/README.md, 「套餐和账单」."
          },
          "state_gb_hours": {
            "type": "number",
            "minimum": 0,
            "description": "Through a control plane only: what the sandboxes kept of their own memory and disk while they slept, sampled once an hour; and what the backups of volumes kept in the object store, the bytes the store holds of each (`stored_bytes`) for as long as it is ready and not gone, booked under the backup's own id (`kind` `backup`: 0 in a group of a sandbox). In GB-hours (a whole number of byte-hours, written as 10^-9 of a GB-hour)."
          },
          "storage_gb_hours": {
            "type": "number",
            "minimum": 0,
            "description": "Through a control plane only: what volumes and snapshots kept, in GB-hours (a whole number of byte-hours, written as 10^-9 of a GB-hour). A volume keeps its `used_bytes` and a snapshot its `size_bytes`, for as long as each is there, counted by its node from the moment it is made to the moment it is deleted. It is booked under the volume's or the snapshot's own id: 0 in a group of a sandbox."
          },
          "log_bytes": {
            "type": "integer",
            "format": "int64",
            "minimum": 0,
            "description": "Through a control plane only: what apps' instances printed that their nodes kept, in bytes of the lines' `text` as they were printed. It is booked to the app itself, in the hour of the lines, under the app's own labels and its id (`metadata.app`), from what each node counted of the app's log: grouped by `metadata.app` it is with what the app's instances used, and grouped by sandbox it is in the group of no sandbox. No meter's: the logs are priced by `log_gb_hours`."
          },
          "log_gb_hours": {
            "type": "number",
            "minimum": 0,
            "description": "Through a control plane only: what apps' instances printed that their nodes kept, times the hours it was kept (each hour from the one it was printed in to the end of the days the account's plan keeps logs), in GB-hours: the logs (`meters.logs` of `GET /v1/billing`). Booked to the app as `log_bytes` is, to the hours it was kept in."
          },
          "drive_write_bytes": {
            "type": "integer",
            "format": "int64",
            "minimum": 0,
            "description": "Through a control plane only: what was written to volumes, as their nodes count it, booked under the volume's own id (0 in a group of a sandbox)."
          },
          "drive_cold_read_bytes": {
            "type": "integer",
            "format": "int64",
            "minimum": 0,
            "description": "Through a control plane only: what was read of volumes from the object store (not from their node's disk), booked under the volume's own id; and what sandboxes read from it through their read-only mounts of Drives' snapshots (slice D7), booked under each sandbox, whose account pays for it."
          },
          "browser_awake_seconds": {
            "type": "number",
            "minimum": 0,
            "description": "Through a control plane only: how long Browsers sessions were awake, in seconds to the millisecond, as their nodes count it, booked of a session and of no other sandbox. No meter prices it: a session is billed by its CPU time, memory and traffic, as any sandbox is."
          },
          "cache_gb_hours": {
            "type": "number",
            "minimum": 0,
            "description": "Through a control plane only: what volumes kept on their nodes' NVMe (the less of the cache each is set to, `cache_bytes`, and the data it holds), in GB-hours, hour by hour, as their nodes count it, booked under the volume's own id (0 in a group of a sandbox): the meter of the cache (`volume_cache`). A volume whose cache is all of it (the default) is counted what it holds."
          },
          "egress_eu_bytes": {
            "type": "integer",
            "format": "int64",
            "minimum": 0,
            "description": "Through a control plane only: of `egress_bytes`, what left from a node in Europe, which is priced at `egress_eu_gb_usd`."
          },
          "egress_ap_bytes": {
            "type": "integer",
            "format": "int64",
            "minimum": 0,
            "description": "Through a control plane only: of `egress_bytes`, what left from a node in Asia-Pacific, priced at `egress_ap_gb_usd`; the rest left from one in North America or with no region, priced at `egress_gb_usd`."
          }
        },
        "additionalProperties": false
      },
      "Prices": {
        "type": "object",
        "additionalProperties": false,
        "description": "In US dollars, with at most six digits after the point: one price list for every product (a Browsers session, a database's instances and Drives are priced as any sandbox and volume are).",
        "required": [
          "cpu_ms_million_usd",
          "memory_gib_hour_usd",
          "requests_million_usd",
          "volume_data_gb_month_usd",
          "volume_cache_gb_month_usd",
          "egress_gb_usd",
          "egress_eu_gb_usd",
          "egress_ap_gb_usd",
          "logs_gb_month_usd",
          "provisional"
        ],
        "properties": {
          "cpu_ms_million_usd": {
            "type": "number",
            "description": "A million CPU-ms of CPU time: milliseconds of a busy vCPU ($0.01, which is $0.036 a vCPU-hour)."
          },
          "memory_gib_hour_usd": {
            "type": "number",
            "description": "A GiB-hour of memory held while awake, or paused in memory (the pause tier)."
          },
          "requests_million_usd": {
            "type": "number",
            "description": "A million requests: into apps, and into sandboxes through their preview URLs."
          },
          "volume_data_gb_month_usd": {
            "type": "number",
            "description": "A GB-month of volume data: what volumes hold, what snapshots keep, what sleeping sandboxes keep of their own memory and disk, and what the backups of volumes keep in the object store (`--price-volume-data-gb-month`). Nothing is priced of what is written to a volume or read of it."
          },
          "volume_cache_gb_month_usd": {
            "type": "number",
            "description": "A GB-month of a volume's NVMe cache: what its node keeps of it on its own disk, the less of the cache the volume is set to (all of it unless said) and the data it holds (`--price-volume-cache-gb-month`). A volume fully cached so costs this and `volume_data_gb_month_usd` together a GB-month of what it holds."
          },
          "egress_gb_usd": {
            "type": "number",
            "description": "A gigabyte (10^9 bytes) sent to the internet from a node in North America, or from one with no region: traffic is priced by the region of the node it left from: `na` North America (and a node with no region), `eu` Europe, `ap` Asia-Pacific."
          },
          "egress_eu_gb_usd": {
            "type": "number",
            "description": "A gigabyte sent to the internet from a node in Europe."
          },
          "egress_ap_gb_usd": {
            "type": "number",
            "description": "A gigabyte sent to the internet from a node in Asia-Pacific."
          },
          "logs_gb_month_usd": {
            "type": "number",
            "description": "A GB-month of what apps print that their nodes keep: the `text` of each line of output kept, counted as it was printed, for the hours it is kept (`--price-logs-gb-month`)."
          },
          "provisional": {
            "type": "boolean",
            "const": false
          }
        }
      },
      "Billing": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "account_id",
          "period",
          "plan",
          "next_plan",
          "included_from",
          "included_to",
          "included_per",
          "meters",
          "list_usd",
          "credits",
          "owed",
          "lots",
          "usage_usd",
          "fee_usd",
          "spend_limit_usd",
          "frozen",
          "frozen_reason",
          "max_vcpus",
          "max_memory_bytes",
          "max_apps",
          "max_app_env",
          "max_volumes",
          "max_volume_bytes",
          "max_volumes_bytes",
          "max_secrets",
          "max_secret_bytes",
          "max_sandbox_secrets_bytes",
          "caps",
          "prices",
          "subscription",
          "debt",
          "trust",
          "statement"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "billing"
          },
          "account_id": {
            "type": "string"
          },
          "period": {
            "type": "string",
            "description": "The month it is, `YYYY-MM` (UTC): the month of `usage_usd` and of `statement`."
          },
          "plan": {
            "type": "string",
            "enum": [
              "free",
              "paid"
            ],
            "x-extensible-enum": true,
            "description": "The plan the account is on now: `free` (no card; quotas of a day, never charged) or `paid` ($5 a month; a quantity of each meter for the period, then the list prices from the balance)."
          },
          "next_plan": {
            "type": "string",
            "enum": [
              "free",
              "paid"
            ],
            "x-extensible-enum": true,
            "description": "The plan it is on once the period it is in is over: `free` where its subscription ends then."
          },
          "included_from": {
            "type": "string",
            "format": "date-time",
            "description": "The stretch what the plan includes is reckoned over: on Paid the period of its subscription the account is in, from its beginning to its end; on Free the day (UTC) it is, whose quotas come back whole at its end (`included_to`)."
          },
          "included_to": {
            "type": "string",
            "format": "date-time"
          },
          "included_per": {
            "type": "string",
            "enum": [
              "day",
              "period"
            ],
            "x-extensible-enum": true,
            "description": "`day`: Free's quotas, of the day (UTC); `period`: Paid's quantities, of the period."
          },
          "meters": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "cpu",
              "memory",
              "requests",
              "volume_data",
              "egress",
              "volume_cache",
              "logs"
            ],
            "properties": {
              "cpu": {
                "$ref": "#/components/schemas/BillingMeter"
              },
              "memory": {
                "$ref": "#/components/schemas/BillingMeter"
              },
              "requests": {
                "$ref": "#/components/schemas/BillingMeter"
              },
              "volume_data": {
                "$ref": "#/components/schemas/BillingMeter",
                "description": "What the account's volumes hold, its snapshots keep, its sleeping sandboxes keep of their own memory and disk, and the backups of its volumes keep: in GB-hours on Paid, in bytes kept at once on Free (a level, not a quota of the day)."
              },
              "egress": {
                "$ref": "#/components/schemas/BillingMeter"
              },
              "volume_cache": {
                "$ref": "#/components/schemas/BillingMeter",
                "description": "What the account's volumes keep on their nodes' own NVMe, the less of the cache each is set to and the data it holds: in GB-hours on Paid, in bytes kept at once on Free (a level)."
              },
              "logs": {
                "$ref": "#/components/schemas/BillingMeter",
                "description": "What the account's apps printed that their nodes kept: on Paid for as long as it is kept, in GB-hours (each line's `text`, counted as it was printed, for the hours it is kept); on Free what was printed in the day, in bytes."
              }
            }
          },
          "list_usd": {
            "type": "number",
            "description": "What the stretch's meters have come to so far at the list prices: their `amount_usd` added up (traffic region by region). 0 on Free."
          },
          "credits": {
            "type": "number",
            "description": "The account's balance: the sum of its ledger's entries (`GET /v1/billing/balance`). It pays for what is used past what a period of Paid includes. Below zero when the account owes."
          },
          "owed": {
            "type": "number",
            "description": "What the account owes: what its sleeping sandboxes, volumes, snapshots and backups kept past what its period of Paid includes while it held nothing. The first thing the next Credits pay. Nothing else is owed: what an account that holds nothing used of the other meters before the stop took hold is written off. An account on Free never owes."
          },
          "lots": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Lot"
            },
            "description": "The Credits it holds, lot by lot, the one that lapses first first: the order they are used in."
          },
          "usage_usd": {
            "type": "number",
            "description": "What the calendar month's usage past what its periods include comes to so far: what the month's statement says (`usage_usd`), which its balance pays. 0 of the hours on Free."
          },
          "fee_usd": {
            "type": "number",
            "description": "The fee of the period the account is in: what was charged for it (0 for one that was given); of a period that is due, what is being collected. 0 on the free plan."
          },
          "spend_limit_usd": {
            "type": [
              "number",
              "null"
            ],
            "description": "The account's spend limit: the most the control plane takes from its card by itself in a calendar month (auto reload, and what is owed, on the billing day); null: none. It stops nothing by itself. An account that never chose one has its plan's."
          },
          "frozen": {
            "type": "boolean",
            "description": "The account's sandboxes are frozen and nothing new is made in its projects, no sandbox, no volume and no snapshot (403 `balance_used_up`; of an account that owes, 402 `account_in_debt` to every call but those that read what it owes and pay it, and those that list and delete what it stores). What it has is kept. What can be deleted is its to delete; while it owes, that is its volumes, its snapshots and its sandboxes, and nothing else."
          },
          "frozen_reason": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "balance_used_up",
              "quota_used_up",
              "account_in_debt",
              null
            ],
            "x-extensible-enum": true,
            "description": "`balance_used_up`: on Paid, the CPU time or the memory its period includes is used up, and the account holds nothing to pay for more; `quota_used_up`: on Free, the day's quota of CPU time or of memory is used up, until 00:00 UTC; `account_in_debt`: it owes (`debt`). null while it is not frozen."
          },
          "max_vcpus": {
            "type": "integer",
            "description": "The largest sandbox the plan lets the account have."
          },
          "max_memory_bytes": {
            "type": "integer"
          },
          "max_apps": {
            "type": "integer",
            "description": "How many apps the plan lets the account have, in all its projects."
          },
          "max_app_env": {
            "type": "integer",
            "description": "How many environment variables and secrets together the plan lets one app have."
          },
          "max_volumes": {
            "type": "integer",
            "description": "How many volumes one project of the account may have (`POST /v1/volumes` beyond it is 403 `volume_limit_reached`): the plan's, or the number an operator gave the account in its place. On Paid a soft limit, which holds back a script that runs away and is raised for an account on request."
          },
          "max_volume_bytes": {
            "type": "integer",
            "description": "How large one of them may be (10 GiB on the free plan, 1 TiB on Paid, which no plan passes; an operator's number for the account, an enterprise customer's, up to 16 TiB, the largest a node makes): a create larger is 403 `volume_limit_reached`, `param` `size_bytes`; one that gives no size is given this, where a node's own (64 GiB) is more."
          },
          "max_volumes_bytes": {
            "type": [
              "integer",
              "null"
            ],
            "description": "How large all of a project's volumes may be together; null: no limit (Paid), what they hold being billed by what is stored."
          },
          "max_secrets": {
            "type": "integer",
            "description": "How many secrets the plan lets one project keep (`PUT /v1/secrets/{name}` of one more is 403 `secret_limit_reached`)."
          },
          "max_secret_bytes": {
            "type": "integer",
            "description": "How large one secret's value may be, in bytes (`PUT /v1/secrets/{name}` of a larger one is 403 `secret_limit_reached`, `param` `value`)."
          },
          "max_sandbox_secrets_bytes": {
            "type": "integer",
            "description": "How many bytes the values of the secrets one sandbox is given may come to together: an instance of an app, given them as environment variables and as files, a secret as often as it is given. A create, change or deploy of an app, and a new value of a secret, that would give its instances more (and more than they are given now) is 403 `secret_limit_reached`. A secret that a sandbox's egress rules send is not given to it: its value goes into the request on its way out."
          },
          "caps": {
            "$ref": "#/components/schemas/CapLimits"
          },
          "prices": {
            "$ref": "#/components/schemas/Prices"
          },
          "subscription": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/Subscription"
              },
              {
                "type": "null"
              }
            ],
            "description": "The account's subscription; null for an account that has none and has asked for none."
          },
          "debt": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/Debt"
              },
              {
                "type": "null"
              }
            ],
            "description": "What the account owes, since when, and when what it stores is deleted for it; null for an account that owes nothing. `GET /v1/account` has the same, and the account's `email`."
          },
          "trust": {
            "$ref": "#/components/schemas/Trust"
          },
          "statement": {
            "type": "string",
            "description": "Where this month's statement is."
          }
        }
      },
      "BillingMeter": {
        "type": "object",
        "additionalProperties": false,
        "description": "One meter of the stretch the account is in (`included_from` to `included_to`): a period of its subscription on Paid, or the day (UTC) on Free.",
        "required": [
          "unit",
          "used",
          "included",
          "left",
          "billable",
          "unit_price_usd",
          "price_per",
          "units_per_price",
          "amount_usd",
          "stopped"
        ],
        "properties": {
          "unit": {
            "type": "string",
            "enum": [
              "cpu_ms",
              "memory_gib_seconds",
              "requests",
              "volume_data_gb_hours",
              "egress_bytes",
              "volume_cache_gb_hours",
              "log_gb_hours",
              "volume_data_bytes",
              "volume_cache_bytes",
              "log_bytes"
            ],
            "x-extensible-enum": true,
            "description": "What the quantities are in. Of a period of Paid: CPU-ms (milliseconds of a busy vCPU), GiB-seconds of memory, requests, GB-hours of volume data and of the NVMe cache, bytes sent, GB-hours of logs kept. Of Free's day, what its quotas count: the same of CPU, memory, requests and traffic; of volume data and of the cache the bytes kept at once (`volume_data_bytes`, `volume_cache_bytes`: a level, as the last whole hour's books have it); of the logs the bytes printed in the day (`log_bytes`)."
          },
          "used": {
            "type": "number",
            "description": "What the account's projects have used in the stretch; of Free's levels, what they keep now."
          },
          "included": {
            "type": [
              "number",
              "null"
            ],
            "description": "What the stretch includes of the meter: Paid's quantity for the period (of volume data, of its cache and of the logs, the GB that may be kept for the whole period, times its hours), or Free's quota of the day (of volume data and of its cache, the level). null: whatever is used is included (traffic, of a plan that includes all that is sent)."
          },
          "left": {
            "type": [
              "number",
              "null"
            ],
            "description": "What is left of `included`; null where `included` is."
          },
          "billable": {
            "type": "number",
            "description": "What of `used` is past what the period includes, and priced. 0 on Free, which is never charged."
          },
          "unit_price_usd": {
            "type": "number",
            "description": "The month's price, for `units_per_price` of `unit`."
          },
          "price_per": {
            "type": "string",
            "enum": [
              "million_cpu_ms",
              "gib_hour",
              "million_requests",
              "gb_month",
              "gb"
            ],
            "x-extensible-enum": true
          },
          "units_per_price": {
            "type": "integer",
            "description": "How many of `unit` the price is for: 1,000,000 (CPU-ms, requests), 3,600 (GiB-seconds), the hours of the stretch (GB-hours), 10^9 (bytes)."
          },
          "amount_usd": {
            "type": "number",
            "description": "`billable` × `unit_price_usd` ÷ `units_per_price`, to the nearest millionth of a dollar, a half going up: what the period has used past what it includes of this meter comes to at the list prices. 0 on Free."
          },
          "regions": {
            "$ref": "#/components/schemas/EgressRegions",
            "description": "Of `egress` alone: the line region by region (the region of the node the traffic left from)."
          },
          "backups_gb_hours": {
            "type": "number",
            "minimum": 0,
            "description": "Of `volume_data` alone, in a period of Paid: how much of `used` the backups of the account's volumes kept in the object store (`/v1/backups`). As the line of volume data of a month's statement says it, and `GET /v1/usage` with `kind=backup` for any range."
          },
          "stopped": {
            "type": "boolean",
            "description": "What the meter serves is stopped. On Paid: what the period includes of it is used up, and the account holds nothing to pay for more. On Free: its quota of the day is used up, whatever the account holds, until 00:00 UTC (`included_to`). `cpu`, `memory`: the account's sandboxes are frozen and nothing new is made (`frozen`). `requests`: the requests for its sandboxes and apps are refused at the edges. `egress`: its sandboxes' traffic to the internet is stopped. `logs`: what its apps print is kept no longer. `volume_data`, `volume_cache`: never: what volumes keep past what a period includes is owed on Paid, and on Free a new volume or snapshot past the level is refused (403 `storage_quota_reached`)."
          }
        }
      },
      "EgressRegions": {
        "type": "object",
        "additionalProperties": false,
        "description": "Of traffic to the internet, region by region: the region of the node it left from: `na` North America (and a node with no region), `eu` Europe, `ap` Asia-Pacific. What a stretch includes of it takes the dearest region's bytes first, then the next (of two at one price, in the order na, eu, ap); each region is priced at its own price and rounded to the nearest millionth on its own, and the line's `billable` and `amount_usd` are the regions' added up (the line's own `unit_price_usd` is North America's).",
        "required": [
          "na",
          "eu",
          "ap"
        ],
        "properties": {
          "na": {
            "$ref": "#/components/schemas/EgressRegionLine"
          },
          "eu": {
            "$ref": "#/components/schemas/EgressRegionLine"
          },
          "ap": {
            "$ref": "#/components/schemas/EgressRegionLine"
          }
        }
      },
      "EgressRegionLine": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "used",
          "billable",
          "unit_price_usd",
          "amount_usd"
        ],
        "properties": {
          "used": {
            "type": "integer",
            "format": "int64",
            "minimum": 0,
            "description": "Bytes sent from the region's nodes (of a statement's part: in the part)."
          },
          "billable": {
            "type": "integer",
            "format": "int64",
            "minimum": 0,
            "description": "What of them is past what is included."
          },
          "unit_price_usd": {
            "type": "number",
            "description": "The region's price of a gigabyte (10^9 bytes)."
          },
          "amount_usd": {
            "type": "number",
            "minimum": 0,
            "description": "What `billable` comes to at that price, to the nearest millionth."
          }
        }
      },
      "Lot": {
        "type": "object",
        "additionalProperties": false,
        "description": "Credits that came together and lapse together.",
        "required": [
          "id",
          "kind",
          "credits",
          "granted",
          "granted_at",
          "expires_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "The id of the ledger's entry that is the lot."
          },
          "kind": {
            "type": "string",
            "enum": [
              "credit",
              "topup",
              "month_grant",
              "period_grant"
            ],
            "x-extensible-enum": true,
            "description": "`credit`: what the operator granted. `topup`: a top-up that was collected. (`month_grant`, `period_grant`: only in a store from before plans included quantities: Credits a calendar month, or a period of a subscription, gave. Nothing gives any now; those that were given pay as the others do and lapse when they would have.)"
          },
          "credits": {
            "type": "number",
            "description": "What is left of it: what it was granted as, less what it has paid for."
          },
          "granted": {
            "type": "number",
            "description": "What it was granted as."
          },
          "granted_at": {
            "type": "string",
            "format": "date-time"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "description": "When it lapses: what is left of it is written off."
          }
        }
      },
      "CapLimits": {
        "type": "object",
        "additionalProperties": false,
        "description": "What the account may have at once, each cap's number and whose it is. `GET /v1/account` has the same with what the account has of each now (`used`), which moves as sandboxes wake, sleep and are made, and is not in the bill (control/README.md). The plan's numbers (awake, their vCPUs, their memory, sleeping, an app's instances at the most and when it names none, made in a minute, made by a project in an hour, apps and versions made in an hour: Free 5, 10, 10 GiB, 20, 5 and 2, 10, 200, 10; Paid 500, 500, 1,000 GiB, 10,000, 500 and 50, 600, 10,000, 500), or an operator's for the account. A cap is a quota and no reservation: within it a node that is full refuses all the same (503 `no_capacity`). It only stops new things: what is awake when it is reached, or when the account comes to a plan with smaller caps, goes on as it is. What is counted is what the nodes say of the account's sandboxes in all its projects, every few seconds (`--usage-seconds`), and what calls made since: a sandbox that its node woke by itself, for a request to one of its ports, and an app's instance that a node made or woke for a request, are counted from its node's next look. What was made in a minute or an hour is counted by the control plane in memory, from its start. Past a cap the account gets 403 `plan_cap_reached`, whose `param` is the cap's name.",
        "required": [
          "instances",
          "vcpus",
          "memory_bytes",
          "sleeping",
          "app_instances",
          "creates_per_minute",
          "creates_per_hour_per_project",
          "app_creates_per_hour"
        ],
        "properties": {
          "instances": {
            "$ref": "#/components/schemas/CapLimit",
            "description": "Sandboxes and app instances awake at once (running or starting)."
          },
          "vcpus": {
            "$ref": "#/components/schemas/CapLimit",
            "description": "The vCPUs of those, together: what each was made with or taken back to (`limits`)."
          },
          "memory_bytes": {
            "$ref": "#/components/schemas/CapLimit",
            "description": "The memory of those, together, in bytes."
          },
          "sleeping": {
            "$ref": "#/components/schemas/CapLimit",
            "description": "Sandboxes kept that do not run (asleep, frozen, failed), not an app's instances: at the limit no sandbox is made and no fork, and a sleep is never refused."
          },
          "app_instances": {
            "$ref": "#/components/schemas/AppInstancesCapLimit"
          },
          "creates_per_minute": {
            "$ref": "#/components/schemas/CapLimit",
            "description": "Sandboxes made in a minute, sliding: a create and a one-shot run one each, a fork as many as it makes. At the limit the refusal is `retryable`, with `Retry-After`."
          },
          "creates_per_hour_per_project": {
            "$ref": "#/components/schemas/CapLimit",
            "description": "Sandboxes one project of the account makes in an hour, sliding, counted as in a minute; each project has an hour of its own, and `used` is the most one of them made. At the limit the refusal is `retryable`, with `Retry-After`; where the minute is past too, the one that holds the call longer is the one named."
          },
          "app_creates_per_hour": {
            "$ref": "#/components/schemas/CapLimit",
            "description": "Apps and versions of apps the account makes in an hour, sliding: an app that is made (with or without an image), a deploy, and a change of an app that makes a version, one each; a call that makes nothing (refused, or a change of what holds at once) is not counted, and a rollback is not. An app the account has no room for is refused for that (`app_limit_reached`) whatever the hour says. At the limit the refusal is `retryable`, with `Retry-After`."
          }
        }
      },
      "CapLimit": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "limit",
          "source"
        ],
        "properties": {
          "limit": {
            "type": "integer",
            "description": "What the account may have."
          },
          "source": {
            "type": "string",
            "enum": [
              "plan",
              "operator"
            ],
            "x-extensible-enum": true,
            "description": "Whose number `limit` is: its plan's, or one an operator set for the account (`PATCH /internal/accounts/{id}`, `caps`)."
          }
        }
      },
      "AppInstancesCapLimit": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "limit",
          "source",
          "default"
        ],
        "description": "An app's instances in a region: the most its `scale.max_instances` and `scale.min_instances` may say (beyond it a create or a change of the app is refused), and what it has when it names no `max_instances`. What its nodes are told it may have is that, and no more than the account has room for awake beside everything else of it that is awake, nor less than it has awake.",
        "properties": {
          "limit": {
            "type": "integer",
            "description": "The most an app may ask for."
          },
          "source": {
            "type": "string",
            "enum": [
              "plan",
              "operator"
            ],
            "x-extensible-enum": true
          },
          "default": {
            "type": "integer",
            "description": "What an app has that names no `max_instances` (or its `min_instances`, where that is more)."
          }
        }
      },
      "Subscription": {
        "type": "object",
        "additionalProperties": false,
        "description": "An account's subscription to Paid ($5 a month). It is paid for a period at a time. The first period begins at the moment the first charge is collected (or the operator gave the subscription) and ends a month on, on the same day of the month, at the beginning of the hour it began in; every period after it begins where the one before ended. A month that has no such day (a subscription of the 31st in April) has its period end on its last day. Back to Free (`PATCH /v1/billing` with `plan` `free`): at the end of the period that is paid for; until then the account is on Paid.",
        "required": [
          "state",
          "plan",
          "since",
          "day",
          "charged",
          "fee_usd",
          "period",
          "next_charge_at",
          "ends_at",
          "charge"
        ],
        "properties": {
          "state": {
            "type": "string",
            "enum": [
              "pending",
              "active",
              "ending",
              "due",
              "unpaid"
            ],
            "x-extensible-enum": true,
            "description": "`pending`: the account asked to subscribe, and the charge of its first period is being collected; it is on the free plan until then. `active`: in a period that is paid for, and the next is charged when it ends. `ending`: in a period that is paid for, at whose end the subscription ends (`ends_at`). `due`: the period that was paid for is over and the charge of the next is being collected. `unpaid`: that charge failed, and is made again (`charge.retry_at`). While `due` or `unpaid` the account is on its plan, held to what its last period was; what it uses is in no period, and is held against what the period that is due includes, whose it is once that is paid for; at `ends_at` the subscription has ended."
          },
          "plan": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "paid",
              null
            ],
            "x-extensible-enum": true,
            "description": "The plan the subscription goes on as: `paid`. While `pending`: the plan that was asked for."
          },
          "since": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When it began: its first period. null while `pending`."
          },
          "day": {
            "type": [
              "integer",
              "null"
            ],
            "description": "The day of the month its periods begin on (in a month that has it); null while `pending`."
          },
          "charged": {
            "type": "boolean",
            "description": "Whether its periods are charged to the account's card. false: the operator gave it, and nothing is charged when a period ends."
          },
          "fee_usd": {
            "type": "number",
            "description": "What a period of it is charged: the fee of `plan`; 0 for one that was given."
          },
          "period": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": false,
            "required": [
              "from",
              "to",
              "paid",
              "charge_id"
            ],
            "description": "The period the account is in; null while `pending`. While `due` or `unpaid`: the period that is not paid for yet.",
            "properties": {
              "from": {
                "type": "string",
                "format": "date-time"
              },
              "to": {
                "type": "string",
                "format": "date-time"
              },
              "paid": {
                "type": "boolean",
                "description": "It is paid for, or was given."
              },
              "charge_id": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "The charge that paid for it, or the one that is being collected for it; null for a period that was given, and while no charge of it is with whoever collects."
              }
            }
          },
          "next_charge_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the charge of the next period is made: the end of the last period that is paid for. null when nothing is to be charged then: the subscription is to end, or was given; and while `pending`, `due` or `unpaid`, when the charge that counts is the one in `charge`."
          },
          "ends_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "`ending`: when the subscription ends, which is the end of the period that is paid for. `due`, `unpaid`: when it has ended unless the charge is collected before: the end of the grace period, seven days (as the control plane is started by default) after the end of the period that was paid for; the account is on the free plan from then. null otherwise."
          },
          "charge": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": false,
            "required": [
              "id",
              "plan",
              "state",
              "failure",
              "retry_at"
            ],
            "description": "While `pending`, `due` or `unpaid`: the last charge that was made for the subscription, the one that is being collected or the one that failed. null otherwise.",
            "properties": {
              "id": {
                "type": "string"
              },
              "plan": {
                "type": [
                  "string",
                  "null"
                ],
                "enum": [
                  "paid",
                  null
                ],
                "x-extensible-enum": true,
                "description": "The plan the charge pays a period of."
              },
              "state": {
                "type": "string",
                "enum": [
                  "to_collect",
                  "collected",
                  "failed"
                ],
                "x-extensible-enum": true
              },
              "failure": {
                "type": [
                  "object",
                  "null"
                ],
                "additionalProperties": false,
                "required": [
                  "code",
                  "message"
                ],
                "properties": {
                  "code": {
                    "type": "string"
                  },
                  "message": {
                    "type": "string"
                  }
                },
                "description": "That it failed, as a charge's `failure` says it to the account: `card_declined` or `payment_failed`, never why; null unless `failed`."
              },
              "retry_at": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time",
                "description": "Of a charge that failed: when the charge is made again; null when it is not."
              }
            }
          }
        }
      },
      "Trust": {
        "type": "object",
        "additionalProperties": false,
        "description": "What the account's trust level lets its top-ups come to (`trust_limit_exceeded`, `trust_limit.reached`): the two amounts in force, each the level's, or one an operator gave the account in its place. Nothing of a level touches what the account holds.",
        "required": [
          "level",
          "set_by",
          "reload_most_usd",
          "month_most_usd",
          "reload_most_set_by",
          "month_most_set_by",
          "month_used_usd",
          "rises_at"
        ],
        "properties": {
          "level": {
            "type": "integer",
            "minimum": 0,
            "maximum": 3,
            "description": "The account's trust level: 0 when it is new, 1 and 2 as its record of payments raises it (`--trust-level-1`, `--trust-level-2`: so many days after its first collected payment, with no dispute and no fraud warning since), 3 as an operator gives it; an operator may set any."
          },
          "set_by": {
            "type": "string",
            "x-extensible-enum": [
              "payment_record",
              "operator"
            ],
            "description": "What set it: the account's record of payments, or an operator (whose level stays until an operator sets another)."
          },
          "reload_most_usd": {
            "type": [
              "number",
              "null"
            ],
            "description": "The most auto reload may take from the account's card in a calendar month, as is in force: the highest `spend_limit_usd` it may set, and the largest `reload_amount_usd`; null: no limit."
          },
          "month_most_usd": {
            "type": [
              "number",
              "null"
            ],
            "description": "The most its top-ups and auto reloads may come to together in a calendar month (UTC), as is in force, counted as they are made: what was collected and what is with the collector or on a payment page, and not what failed or was taken back, nor what was charged on its billing day for what it owed. Null: no limit."
          },
          "reload_most_set_by": {
            "type": "string",
            "x-extensible-enum": [
              "level",
              "operator"
            ],
            "description": "Whose `reload_most_usd` is: the level's, or an operator's for this account, which holds at every level until an operator takes it back."
          },
          "month_most_set_by": {
            "type": "string",
            "x-extensible-enum": [
              "level",
              "operator"
            ],
            "description": "Whose `month_most_usd` is, as `reload_most_set_by` says of the other."
          },
          "month_used_usd": {
            "type": "number",
            "description": "What they come to this month so far, counted so."
          },
          "rises_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the level rises by itself next, if no payment is disputed or warned of as a fraud before then: the first look of that day gives it. Null: it does not (an operator set it, the account never paid, a payment was disputed or warned of, or no level above it comes by itself)."
          }
        }
      },
      "BillingUpdate": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "plan": {
            "type": "string",
            "enum": [
              "free",
              "paid"
            ],
            "description": "`paid`: the account subscribes to Paid, $5 a month: the charge of its first period is made at once (`GET /v1/billing/charges`, `kind` `subscription`), and it is on Paid from the moment it is collected; asked while a subscription that is to end goes on, it goes on after all, and nothing is charged. `free`: the subscription ends at the end of the period that is paid for, and nothing is paid back; until then the account is on Paid."
          },
          "spend_limit_usd": {
            "type": [
              "number",
              "null"
            ],
            "minimum": 0,
            "description": "The most the control plane takes from the card by itself in a calendar month (auto reload, and what is owed, on the billing day), in US dollars (held to the millionth); null: no limit."
          },
          "start_now": {
            "type": "boolean",
            "description": "Of a `plan` that is bought now (the account has no subscription that goes on, or moves up): `true`: the account asks for what it buys to be provided at once, within the 14 days in which a consumer in the European Economic Area or the United Kingdom may withdraw from it, and acknowledges that it pays for what was provided if it withdraws and that a top-up whose Credits are all used has been provided in full and can no longer be withdrawn from (`GET /v1/billing/withdrawals`, `right.start_now`, has the words). Required of an account that buys as a consumer there (no tax id that is verified or being verified, and its billing address there, or, without one, the address the call comes from): 400 `start_now_required` without it. Kept with the purchase wherever it is given: without it, a withdrawal gives back all that was paid (control/README.md, 撤回)."
          }
        }
      },
      "BillingStatement": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "account_id",
          "period",
          "from",
          "to",
          "ended",
          "hours",
          "lines",
          "parts",
          "earlier_usd",
          "usage_usd",
          "fees",
          "fee_usd",
          "projects",
          "prices",
          "databases",
          "credits_debited",
          "written_off_usd",
          "paid_from",
          "granted",
          "lapsed",
          "restored",
          "taken_back",
          "credits_balance",
          "closed"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "billing_statement"
          },
          "account_id": {
            "type": "string"
          },
          "period": {
            "type": "string"
          },
          "from": {
            "type": "string",
            "format": "date-time"
          },
          "to": {
            "type": "string",
            "format": "date-time"
          },
          "ended": {
            "type": "boolean"
          },
          "hours": {
            "type": "integer",
            "description": "The hours the calendar month has."
          },
          "lines": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/StatementLine"
            },
            "description": "One line for each meter, of the whole month: cpu, memory, requests, volume_data, egress, volume_cache, logs."
          },
          "parts": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/StatementPart"
            },
            "description": "What the month is made of: the periods of the subscription that have hours in it, the earliest first, and then the hours in no period (left out where the periods cover the month and nothing was used outside them)."
          },
          "earlier_usd": {
            "type": "number",
            "description": "0. (Of a month in which a store was brought from plans that included use by the month: what the month owed by then, which the lines do not price again.)"
          },
          "usage_usd": {
            "type": "number",
            "description": "What the month's usage past what its periods include comes to: the parts' `usage_usd` added up (with `earlier_usd`). This is what the month takes from the account's balance, as far as the balance goes. Nothing of the hours on Free."
          },
          "fees": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/StatementFee"
            },
            "description": "One for each period of the subscription that began in the month; empty where none did."
          },
          "fee_usd": {
            "type": "number",
            "description": "The fees added up: what was charged to the card for them. It is not in `usage_usd`."
          },
          "projects": {
            "type": "array",
            "description": "Every project of the account, in the order they were made, with what it used in the month: the totals `GET /v1/usage` gives that project's key for the month.",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "id",
                "name",
                "cpu_ms",
                "memory_gib_seconds",
                "requests",
                "volume_data_gb_hours",
                "egress_bytes",
                "volume_cache_gb_hours",
                "log_gb_hours",
                "log_bytes"
              ],
              "properties": {
                "id": {
                  "type": "string"
                },
                "name": {
                  "type": "string"
                },
                "cpu_ms": {
                  "type": "number",
                  "minimum": 0,
                  "description": "CPU time, in CPU-ms: milliseconds of a busy vCPU."
                },
                "memory_gib_seconds": {
                  "type": "number"
                },
                "requests": {
                  "type": "integer"
                },
                "volume_data_gb_hours": {
                  "type": "number",
                  "description": "What the project's volumes, snapshots, sleeping sandboxes and the backups of its volumes kept."
                },
                "egress_bytes": {
                  "type": "integer"
                },
                "volume_cache_gb_hours": {
                  "type": "number",
                  "description": "What the project's volumes kept on their nodes' NVMe."
                },
                "log_gb_hours": {
                  "type": "number",
                  "description": "What the project's apps printed that their nodes kept, times the hours it was kept: the logs' meter."
                },
                "log_bytes": {
                  "type": "integer",
                  "description": "What the project's apps printed that their nodes kept, in bytes, in the hours it was printed in: no meter's."
                }
              }
            }
          },
          "prices": {
            "$ref": "#/components/schemas/Prices"
          },
          "databases": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/StatementDatabase"
            },
            "description": "Every database of the account that used anything in the month (a deleted one too), in the order they were made: a line of its own for each (slice DB7)."
          },
          "credits_debited": {
            "type": "number",
            "description": "What the month's usage took from the account's balance: the month's entry of the ledger, which follows `usage_usd` less `written_off_usd` and is behind by seconds at the most while the month is not closed."
          },
          "written_off_usd": {
            "type": "number",
            "description": "What the month's usage came to that no lot could pay and that is owed by nobody: what an account that held nothing used of the meters that are stopped (CPU time, memory, requests, traffic, logs kept) between what its period includes running out and the stop taking hold. With `credits_debited` it is `usage_usd`, once a look has brought the ledger up to the statement."
          },
          "paid_from": {
            "type": "object",
            "additionalProperties": {
              "type": "number"
            },
            "required": [
              "credit",
              "topup",
              "unpaid"
            ],
            "description": "Which Credits paid for the month's usage, by the kind of lot (`Lot.kind`), and what none has paid (`unpaid`: it is owed, and the next Credits pay it first). They add up to `credits_debited`. A kind that only a store from before plans included quantities has (`month_grant`, `period_grant`) is there where something of it paid; and `debt_written_off` where what was owed for the month was written off when what the account stored had been deleted (it is among what adds up, and nobody paid it).",
            "properties": {
              "credit": {
                "type": "number"
              },
              "topup": {
                "type": "number"
              },
              "unpaid": {
                "type": "number"
              },
              "debt_written_off": {
                "type": "number"
              }
            }
          },
          "granted": {
            "type": "object",
            "additionalProperties": {
              "type": "number"
            },
            "required": [
              "credit",
              "topup"
            ],
            "properties": {
              "credit": {
                "type": "number"
              },
              "topup": {
                "type": "number"
              }
            },
            "description": "The Credits that were granted and bought in the month, by the kind of lot."
          },
          "lapsed": {
            "type": "object",
            "additionalProperties": {
              "type": "number"
            },
            "required": [
              "credit",
              "topup"
            ],
            "properties": {
              "credit": {
                "type": "number"
              },
              "topup": {
                "type": "number"
              }
            },
            "description": "What was left of lots that lapsed with the month, by the kind of lot: written off, in entries of this month (`expiry`)."
          },
          "restored": {
            "type": "object",
            "additionalProperties": {
              "type": "number"
            },
            "required": [
              "credit",
              "topup"
            ],
            "properties": {
              "credit": {
                "type": "number"
              },
              "topup": {
                "type": "number"
              }
            },
            "description": "What an operator brought back in the month of lots that had lapsed, in it or before, by the kind of lot (`restore` entries of this month). What lapsed is said in the month it lapsed with all the same."
          },
          "taken_back": {
            "type": "number",
            "description": "What was taken from the balance in the month with top-ups that were taken back (`refund` entries)."
          },
          "credits_balance": {
            "type": "number",
            "description": "What the account held at the month's end, as the ledger has it: the sum of its entries of this month and of the months before. Of the month it is: what it holds. Below zero when the account owes."
          },
          "closed": {
            "type": "boolean",
            "description": "The month is over, what its usage comes to was taken from the account's balance as this statement has it (`credits_debited` and `written_off_usd` are `usage_usd`), and what was left of the lots that lapsed in it is written off. False for the month it is; false in the seconds after a month's end, until what was used in it is in the books (ten minutes at the most, when a node is not read); and false again while usage of the month that came in later waits to be taken, which is seconds."
          }
        }
      },
      "StatementLine": {
        "type": "object",
        "additionalProperties": false,
        "description": "A meter, of the whole calendar month.",
        "required": [
          "item",
          "unit",
          "quantity",
          "billable",
          "unit_price_usd",
          "price_per",
          "amount_usd"
        ],
        "properties": {
          "item": {
            "type": "string",
            "enum": [
              "cpu",
              "memory",
              "requests",
              "volume_data",
              "egress",
              "volume_cache",
              "logs"
            ],
            "x-extensible-enum": true
          },
          "unit": {
            "type": "string",
            "description": "What the quantities are in, as `BillingMeter.unit`."
          },
          "quantity": {
            "type": "number",
            "description": "What the account's projects used in the month."
          },
          "billable": {
            "type": "number",
            "description": "What of it was past what its stretch includes: the parts' added up."
          },
          "unit_price_usd": {
            "type": "number"
          },
          "price_per": {
            "type": "string",
            "enum": [
              "million_cpu_ms",
              "gib_hour",
              "million_requests",
              "gb_month",
              "gb"
            ],
            "x-extensible-enum": true
          },
          "amount_usd": {
            "type": "number",
            "description": "The parts' amounts added up."
          },
          "regions": {
            "$ref": "#/components/schemas/EgressRegions",
            "description": "Of `egress` alone: the line region by region (the region of the node the traffic left from)."
          },
          "backups_gb_hours": {
            "type": "number",
            "minimum": 0,
            "description": "Of the line of `volume_data` alone: how much of `quantity` the backups of the account's volumes kept in the object store (`GET /v1/usage` with `kind=backup` says it for any range); the rest is what its volumes, snapshots and sleeping sandboxes kept. One meter at one price."
          }
        }
      },
      "StatementPart": {
        "type": "object",
        "additionalProperties": false,
        "description": "A part of a calendar month: what of it is of one stretch. A period of the subscription that has hours in the month, or the hours of the month that are in no period, which are Free's and priced at nothing. Of each: its lines, and what they come to past what its period includes (`usage_usd`).",
        "required": [
          "from",
          "to",
          "plan",
          "period",
          "hours",
          "lines",
          "usage_usd"
        ],
        "properties": {
          "from": {
            "type": "string",
            "format": "date-time",
            "description": "Where the part begins in the month: the period's beginning, or the month's."
          },
          "to": {
            "type": "string",
            "format": "date-time"
          },
          "plan": {
            "type": "string",
            "enum": [
              "free",
              "paid"
            ],
            "x-extensible-enum": true
          },
          "period": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": false,
            "required": [
              "from",
              "to"
            ],
            "description": "The period of the subscription the part is of, from its beginning to its end; null for the hours in no period.",
            "properties": {
              "from": {
                "type": "string",
                "format": "date-time"
              },
              "to": {
                "type": "string",
                "format": "date-time"
              }
            }
          },
          "hours": {
            "type": "integer",
            "description": "The hours the stretch has (a period: from the hour it begins in up to the hour it ends in; otherwise the calendar month's): what its volume data, its cache and its logs are counted by."
          },
          "lines": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/StatementPartLine"
            },
            "description": "One line for each meter: cpu, memory, requests, volume_data, egress, volume_cache, logs."
          },
          "usage_usd": {
            "type": "number",
            "description": "What the part's lines come to: what was used past what its period includes, at the list prices; 0 of the hours in no period."
          }
        }
      },
      "StatementPartLine": {
        "type": "object",
        "additionalProperties": false,
        "description": "A meter, of one part of the calendar month. `amount_usd` is what `billable_earlier` + `billable` cost (× `unit_price_usd` ÷ `units_per_price`, to the nearest millionth of a dollar, a half going up) less what `billable_earlier` costs alone: so the two months a period lies in add up to what the period owes, to the millionth.",
        "required": [
          "item",
          "unit",
          "quantity",
          "earlier",
          "included",
          "billable",
          "billable_earlier",
          "unit_price_usd",
          "price_per",
          "units_per_price",
          "amount_usd"
        ],
        "properties": {
          "item": {
            "type": "string",
            "enum": [
              "cpu",
              "memory",
              "requests",
              "volume_data",
              "egress",
              "volume_cache",
              "logs"
            ],
            "x-extensible-enum": true
          },
          "unit": {
            "type": "string",
            "description": "What the quantities are in, as `BillingMeter.unit`."
          },
          "quantity": {
            "type": "number",
            "description": "What was used in the part."
          },
          "earlier": {
            "type": "number",
            "description": "What was used of the same period in the month before; 0 for a part that is of no period, and for a period that began in this month."
          },
          "included": {
            "type": [
              "number",
              "null"
            ],
            "description": "What the part's period includes of the meter, as `BillingMeter.included`; null of a part in no period (Free's hours, at nothing)."
          },
          "billable": {
            "type": "number",
            "description": "What of `quantity` is past what is included: `earlier` + `quantity` past `included`, less `earlier` past it. 0 of a part that is in no period: Free's hours, at nothing."
          },
          "billable_earlier": {
            "type": "number",
            "description": "What was past what is included of the same period in the month before."
          },
          "unit_price_usd": {
            "type": "number"
          },
          "price_per": {
            "type": "string",
            "enum": [
              "million_cpu_ms",
              "gib_hour",
              "million_requests",
              "gb_month",
              "gb"
            ],
            "x-extensible-enum": true
          },
          "units_per_price": {
            "type": "integer",
            "description": "How many of `unit` the price is for: 1,000,000 (CPU-ms, requests), 3,600 (GiB-seconds), `hours` of the part (GB-hours), 10^9 (bytes)."
          },
          "amount_usd": {
            "type": "number"
          },
          "regions": {
            "$ref": "#/components/schemas/EgressPartRegions",
            "description": "Of `egress` alone: the line region by region (the region of the node the traffic left from), with what of each region was past what is included before the part."
          }
        }
      },
      "EgressPartRegions": {
        "type": "object",
        "additionalProperties": false,
        "description": "Of traffic to the internet in a part of a statement, region by region, as `EgressRegions`, and of each region also what was past what is included before the part (`billable_earlier`): a region's amount is what its `billable_earlier` and `billable` cost together at its price, less what its `billable_earlier` costs alone, each to the nearest millionth. The line's `billable` and `billable_earlier` are the regions' added up, and so is its `amount_usd`.",
        "required": [
          "na",
          "eu",
          "ap"
        ],
        "properties": {
          "na": {
            "$ref": "#/components/schemas/EgressRegionPartLine"
          },
          "eu": {
            "$ref": "#/components/schemas/EgressRegionPartLine"
          },
          "ap": {
            "$ref": "#/components/schemas/EgressRegionPartLine"
          }
        }
      },
      "EgressRegionPartLine": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "used",
          "billable",
          "billable_earlier",
          "unit_price_usd",
          "amount_usd"
        ],
        "properties": {
          "used": {
            "type": "integer",
            "format": "int64",
            "minimum": 0,
            "description": "Bytes sent from the region's nodes in the part."
          },
          "billable": {
            "type": "integer",
            "format": "int64",
            "minimum": 0,
            "description": "What of them is past what is included."
          },
          "billable_earlier": {
            "type": "integer",
            "format": "int64",
            "minimum": 0,
            "description": "What of the region's bytes was past what is included of the same stretch before the part (in the month before)."
          },
          "unit_price_usd": {
            "type": "number",
            "description": "The region's price of a gigabyte (10^9 bytes)."
          },
          "amount_usd": {
            "type": "number",
            "minimum": 0,
            "description": "What `billable_earlier` and `billable` cost together at that price, less what `billable_earlier` costs alone, to the nearest millionth."
          }
        }
      },
      "StatementFee": {
        "type": "object",
        "additionalProperties": false,
        "description": "The fee of a period of the subscription that began in the month. It was charged to the card, and is no part of what the balance pays.",
        "required": [
          "from",
          "to",
          "plan",
          "charge_id",
          "amount_usd"
        ],
        "properties": {
          "from": {
            "type": "string",
            "format": "date-time",
            "description": "When the period begins."
          },
          "to": {
            "type": "string",
            "format": "date-time",
            "description": "When it ends: earlier than a month on for a period that was cut short (a withdrawal)."
          },
          "plan": {
            "type": "string",
            "enum": [
              "paid"
            ],
            "x-extensible-enum": true
          },
          "charge_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "The charge that paid it; null for a period that was given."
          },
          "amount_usd": {
            "type": "number",
            "description": "What was charged for the period: Paid's fee; 0 for one that was given."
          }
        }
      },
      "StatementDatabase": {
        "type": "object",
        "additionalProperties": false,
        "description": "A database of the account with what it used in the month, a line of its own (slice DB7) in the place of the usage grouped by database times the prices. Its quantities are its branches' instances' and Drives' (what `GET /v1/usage?group_by=database` gives for the month), in the units of the statement's lines; `list_usd` is what they come to at the month's list prices, each meter's priced as a line's amount is, to the nearest millionth (traffic region by region), added up. What is billed of it past what the plan includes is in the statement's lines, which have every database's usage with the rest.",
        "required": [
          "id",
          "name",
          "project_id",
          "cpu_ms",
          "memory_gib_seconds",
          "requests",
          "volume_data_gb_hours",
          "egress_bytes",
          "volume_cache_gb_hours",
          "log_gb_hours",
          "history_gb_hours",
          "tiered_gb_hours",
          "list_usd"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "project_id": {
            "type": "string"
          },
          "cpu_ms": {
            "type": "number",
            "minimum": 0,
            "description": "CPU time, in CPU-ms: milliseconds of a busy vCPU."
          },
          "memory_gib_seconds": {
            "type": "number",
            "description": "Their memory, awake and paused."
          },
          "requests": {
            "type": "integer"
          },
          "volume_data_gb_hours": {
            "type": "number",
            "description": "What its branches' Drives kept and its sleeping instances kept, with its history past its free day (`history_gb_hours`) and its tiered partitions (`tiered_gb_hours`)."
          },
          "egress_bytes": {
            "type": "integer",
            "description": "What they sent to the internet."
          },
          "volume_cache_gb_hours": {
            "type": "number",
            "description": "What its branches' Drives kept on their nodes' NVMe."
          },
          "log_gb_hours": {
            "type": "number"
          },
          "history_gb_hours": {
            "type": "number",
            "minimum": 0,
            "description": "Of `volume_data_gb_hours`: what its history kept older than its free last day and within the window it keeps (its root branch's window's old versions and its archived WAL)."
          },
          "tiered_gb_hours": {
            "type": "number",
            "minimum": 0,
            "description": "Of `volume_data_gb_hours`: what its tiered partitions kept, by their size before they were moved."
          },
          "list_usd": {
            "type": "number",
            "minimum": 0,
            "description": "What its quantities come to at the month's list prices: each meter's priced as a line's amount is, to the nearest millionth (traffic region by region), added up."
          }
        }
      },
      "AppCreate": {
        "type": "object",
        "description": "Every field may be left out, and `null` is as if the field were not given. What a version of a service is made of besides an app's (`volumes`, `kind`, `files`, `user`, `working_dir`, `stop`, `hostname`) is kept with each version, and the nodes are not told of it: no instance has it. `depends_on` is kept with the app as it is written (the services it names are not looked up), and orders no deploy.",
        "additionalProperties": false,
        "properties": {
          "name": {
            "type": [
              "string",
              "null"
            ],
            "description": "1 to 40 lower-case letters, digits and hyphens, beginning with a letter, with no hyphen at its end or two in a row, not beginning with `app-` or `sbx-`, and not `localhost`, `internal` or `shared`. It is the name the project's sandboxes on its network find the app by (`private`), and no other app's `network.aliases` (409 `name_taken`). Not given: one is made up."
          },
          "image": {
            "type": [
              "string",
              "null"
            ],
            "description": "An image reference: `ghcr.io/acme/api:1.4`, `python:3.12`, `...@sha256:...`. Pulled from a registry on the internet: one that gives the image to anybody, or one that wants a login, with `image_pull_secret`."
          },
          "image_pull_secret": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 128,
            "description": "The name of one of the project's secrets (`/v1/secrets`) that holds the credentials of the image's registry, for an image that is not public: `username:password` in printable ASCII, the user name ending at the first colon; a registry that takes a token alone documents the user name that goes with it (`AWS` for ECR, `oauth2accesstoken` for Google's, any name for GHCR). The value goes to each node that builds a version, with the image, and there to the tool that pulls it and to nothing else: nothing of it is in an instance, in a template, in an event or in a log, and what a registry says when it refuses it is the version's `error`, without it (`registry_auth_failed`). Naming it takes `write`, and no `admin`. The secret has to exist (404 `secret_not_found`) and to hold such a pair (400 `invalid_field`) when a version is made; a deploy reads it when it asks a node. In a `PATCH`, `null` takes it away. Not given: none, and the image is asked of its registry without a login."
          },
          "start_command": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/StartCommand"
              },
              {
                "type": "null"
              }
            ],
            "description": "Not given: the image's own (ENTRYPOINT and CMD)."
          },
          "start_args": {
            "anyOf": [
              {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "minItems": 1,
                "maxItems": 1000
              },
              {
                "type": "null"
              }
            ],
            "description": "The arguments that follow the image's ENTRYPOINT in the place of its CMD, as a Compose service's `command` does: the node puts the image's ENTRYPOINT in front of them (an image with none runs them alone). Not with `start_command`, which is the whole start command (400 `invalid_field`). Not given: the image's CMD."
          },
          "port": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 1,
            "maximum": 65535,
            "description": "The port the service listens on. Not given: the one port the image declares (EXPOSE), or 8080 when it declares none or several."
          },
          "ready": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Ready"
              },
              {
                "type": "null"
              }
            ],
            "description": "Not given: `{\"tcp\": true}`."
          },
          "env": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": {
              "type": "string"
            },
            "description": "The environment of the start command, after the image's own. A value may refer to a variable of another service of the project, or of the app itself, or to one of the project's shared variables, alone or in other text: `${{ db.POSTGRES_USER }}`, `${{ shared.REGION }}`. A service's variables are its `env` (whose own references are followed), its `secrets`, and the four the platform gives it (`SBX_SERVICE`, its name; `SBX_PRIVATE_DOMAIN`, `<name>.internal`; `SBX_PRIVATE_ADDRESS`, its address on the project's network; `SBX_PROJECT`, the project's id). A version holds what they come to when it is made: the app keeps what was written. A reference to a secret makes the variable that secret, as `secrets` does: it is the whole of a value, and it takes `admin` where the app does not have the secret. Every `${{` begins a reference (400 `invalid_reference`, `param` at the variable); one that goes round, or refers to a service or a variable that is not there, is refused when a version is made (400 `invalid_reference`), and one to a secret that is gone too (404 `secret_not_found`)."
          },
          "secrets": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": {
              "type": "string"
            },
            "description": "Environment variable to the name of one of the project's secrets (`/v1/secrets`): the secret's value is in the environment of the start command. No variable is in `env` as well."
          },
          "size": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Size"
              },
              {
                "type": "null"
              }
            ],
            "description": "Not given: the free plan's largest sandbox, or the account's plan's if that is smaller."
          },
          "regions": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "string",
              "enum": [
                "na",
                "eu",
                "ap"
              ]
            },
            "minItems": 1,
            "description": "Not given: all of them."
          },
          "scale": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Scale"
              },
              {
                "type": "null"
              }
            ]
          },
          "keys": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/AppKeys"
              },
              {
                "type": "null"
              }
            ]
          },
          "egress": {
            "$ref": "#/components/schemas/EgressRules",
            "description": "Egress rules as a sandbox has them (`default`, `allow`, `deny`, `http`, `inject`), which every instance of the app's group has (see `App`)."
          },
          "metadata": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": {
              "type": "string"
            },
            "maxProperties": 60,
            "description": "Labels: the app's instances carry them. At most 60 keys of up to 64 characters, with values of up to 1024."
          },
          "logs": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/AppLogSettings"
              },
              {
                "type": "null"
              }
            ],
            "description": "What the app's log is to have (see `App`). Not given: every part at its default."
          },
          "traces": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/AppTraceSettings"
              },
              {
                "type": "null"
              }
            ],
            "description": "What the app's requests' traces are to have (see `App`). Not given: none."
          },
          "public": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "Whether the app answers at its default domain on the internet (`url`). `false`: it has none, and is reached on its project's network, and at the customers' domains that lead to it. Holds at once. Not given: `true`."
          },
          "direct": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "Whether the app answers at a name that does not go through the proxy as well (`App.direct`). Null: not."
          },
          "checkpoint": {
            "oneOf": [
              {
                "type": "object",
                "properties": {
                  "every_seconds": {
                    "type": [
                      "integer",
                      "null"
                    ],
                    "minimum": 1,
                    "maximum": 600,
                    "description": "Left out or null: 10."
                  }
                },
                "additionalProperties": false
              },
              {
                "type": "null"
              }
            ],
            "description": "Checkpoints of its keyed instances while they run (`App.checkpoint`). Null: none."
          },
          "drives": {
            "type": [
              "array",
              "null"
            ],
            "maxItems": 8,
            "items": {
              "$ref": "#/components/schemas/ServiceDriveGiven"
            },
            "description": "The Drives its instance mounts read-write (slice D8), kept with each version made after: Drives of the project's, by name or id as written, each looked up when the app is made or changed and when a version is deployed (404 `drive_not_found`; a volume of another kind is none). A service with a Drive is stateful, as one with a volume of its own is (`volumes`): one instance, which has its Drives, made on the node that holds them, or on another of their region they are brought to. A Drive has one writer: an app of many instances (`scale.max_instances` other than 1 or null, `scale.min_instances` above 1, or `keys`) is refused, 409 `drive_single_writer`; read-only mounts of a Drive's snapshots, which many may have, are a sandbox's (`mode` `snapshot`, slice D7) and no app's version mounts one yet, and a keyed app gives each key a Drive of its own (`key_drives`). No two of its volumes and Drives at one path; an instance mounts 8 Drives and volumes of its own at the most (409 `too_many_drives`). Not given: none."
          },
          "key_drives": {
            "type": [
              "array",
              "null"
            ],
            "maxItems": 8,
            "items": {
              "$ref": "#/components/schemas/KeyDriveGiven"
            },
            "description": "The Drives each key's instance has of its own (slice D8; reports/workers/keyed-upgrade.md, where they were `key_volumes`), of a keyed app alone (`keys`; 400 `invalid_field` otherwise), kept with each version made after. A key's Drives are made when its first instance is made, on the node it is made on and in its region, and mounted before its start command runs (its versions are built with the command held back); every instance the key has after (its first deleted idle, or by its id) mounts the same Drives, by their names; an upgrade of its instance to a version that names other ones mounts them by their names too: one the version names that the key has none of is made then, one it no longer names is left off and kept (`POST /v1/apps/{app}/instances/{key}/upgrade`); they are deleted with the key (`DELETE /v1/apps/{app}/instances/{key}`) or with the app. A key's Drive is shown in `GET /v1/volumes` with its `app_id` and `key`, is billed as any Drive, counts toward what the project's Drives hold together and not toward their number, and is not the project's to delete or to mount elsewhere (409 `key_drive`). Not given: none."
          },
          "volumes": {
            "type": [
              "array",
              "null"
            ],
            "maxItems": 16,
            "items": {
              "$ref": "#/components/schemas/ServiceVolumeGiven"
            },
            "description": "What its instances mount, kept with each version made after: volumes of the project's (`POST /v1/volumes`), by name or id as written, each looked up when the app is made or changed and when a version is deployed (404 `volume_not_found`). No two at one path. A service with a volume of its own (one that is not shared) is stateful: it has one instance, which has the volume (`scale.max_instances` 1 or null, `scale.min_instances` 0 or 1, no `keys`: 400 `volume_single_instance`), on the machine of its volumes, which are on one machine (409 `volumes_apart`). Its versions are built there with the start command held back; the instance is made at the service's address on the project's network, named as the service is (or as `hostname` says), mounts the volumes first (a new one given what the image has there, unless `nocopy`), then starts the command and takes connections once it is up as `ready` says; every start of it from its disk does the same. It sleeps as any keyed instance does (`scale.sleep_after_ms`; never with a floor of 1), is woken by a connection on the project's network, and is deleted by its owner alone, or with the app. A version deployed later replaces it: the old one's command is stopped as `stop` says, the new one is made with the same volumes at the same address, and the traffic is the new one's when it is up; one that does not come up has the version before made again with the volumes as they were left, and the deploy fails with what the new one said (`app.deploy_failed`). Not given: none."
          },
          "depends_on": {
            "type": [
              "array",
              "null"
            ],
            "maxItems": 100,
            "items": {
              "$ref": "#/components/schemas/ServiceDependencyGiven"
            },
            "description": "The services of the project whose deploy this one's waits for: each once. Holds at once. Not given: none."
          },
          "network": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/ServiceNetworkGiven"
              },
              {
                "type": "null"
              }
            ],
            "description": "How the project's sandboxes find it on its network besides its name. Holds at once: the nodes are told. In a `PATCH`, merged a key at a time. Not given: no other name, and its names answer its address."
          },
          "kind": {
            "anyOf": [
              {
                "type": "string",
                "enum": [
                  "service",
                  "job"
                ]
              },
              {
                "type": "null"
              }
            ],
            "description": "`service`: it runs for as long as it is there; `job`: it runs to its end. Not given: `service`."
          },
          "files": {
            "type": [
              "array",
              "null"
            ],
            "maxItems": 64,
            "items": {
              "$ref": "#/components/schemas/ServiceFileGiven"
            },
            "description": "What is written into its instances before the start command runs: no two at one path, and a mebibyte of text in all at the most. A file that holds a secret gives the app the secret: that takes `admin`, as `secrets` does, and the secret has to be there. Not given: none."
          },
          "user": {
            "type": [
              "string",
              "null"
            ],
            "minLength": 1,
            "maxLength": 256,
            "description": "The user the start command runs as, by name or number (`postgres`, `999`, `999:999`). Not given: the image's own."
          },
          "working_dir": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^/",
            "maxLength": 4096,
            "description": "The directory the start command runs in. Not given: the image's own."
          },
          "stop": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/ServiceStopGiven"
              },
              {
                "type": "null"
              }
            ],
            "description": "How an instance is stopped. In a `PATCH`, merged a key at a time. Not given: the image's own stop signal, else SIGTERM, and ten seconds."
          },
          "hostname": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$",
            "description": "The machine name of the instance of a service that has a volume. Not given: the service's name."
          },
          "runtime": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/RuntimeGiven"
              },
              {
                "type": "null"
              }
            ],
            "description": "The service's runtime settings: limits, sysctls, tmpfs mounts, the resolver, /dev/shm's size. Replaced whole in a `PATCH`, and `null` takes them away. Not given: none (open files 1024, raised to 524288 at the most; /dev/shm half of the memory; the guest's own resolver)."
          }
        }
      },
      "AppLogSettings": {
        "type": "object",
        "additionalProperties": false,
        "description": "What an app's log is to have. Each part may be left out, and is at its default then; in a `PATCH` the parts are merged into what there is, and `null` takes a part back to its default. It holds at once and makes no version: the machines that serve the app go by it from the moment they are told, which is within seconds.",
        "properties": {
          "output": {
            "type": [
              "boolean",
              "null"
            ],
            "default": true,
            "description": "Whether what the instances print is in the log. false: the machines do not read it; the instances print as before, and an instance keeps the last 16 MiB of it, which is read inside it (`GET /v1/sandboxes/{id}/exec/exe-start`). What was printed while it was false is not in the log when it is true again."
          },
          "keep": {
            "type": [
              "boolean",
              "null"
            ],
            "default": true,
            "description": "Whether the output is kept for good. false: it is in the log on the machines as before, and counted as not kept (`keep_off` of `GET /v1/apps/{app}/logs/usage`); the log says where that begins (`logs.keep_off`) and ends (`logs.keep_on`)."
          },
          "requests": {
            "anyOf": [
              {
                "type": "object",
                "additionalProperties": false,
                "properties": {
                  "sample_rate": {
                    "type": [
                      "number",
                      "null"
                    ],
                    "minimum": 0,
                    "maximum": 1,
                    "default": 1,
                    "description": "The share of the requests whose line is kept, each drawn by itself. Below 1, a line that is kept says the share it stands for (`sample_rate`), and the requests whose line was left out are counted (`sampled`)."
                  },
                  "keep_errors": {
                    "type": [
                      "boolean",
                      "null"
                    ],
                    "default": true,
                    "description": "A request that failed (status 500 or above, no answer, refused by the machine) has its line whatever the share; such a line says `sample_rate` 1."
                  }
                }
              },
              {
                "type": "null"
              }
            ]
          }
        }
      },
      "AppTraceSettings": {
        "type": "object",
        "additionalProperties": false,
        "description": "What an app's requests' traces are to have (W3C Trace Context; OpenTelemetry). Each part may be left out, and is at its default then; in a `PATCH` the parts are merged into what there is, and `null` takes a part back to its default. It holds at once and makes no version: the machines that serve the app go by it from the moment they are told, within seconds.",
        "properties": {
          "sample_rate": {
            "type": [
              "number",
              "null"
            ],
            "minimum": 0,
            "maximum": 1,
            "default": 0,
            "description": "The share of the app's requests the machine that takes one makes a span for: from when it has read the request's head to when the last byte of its answer has gone, with the request's method, path, status and id, and the instance's wake and an instance made for it as its events. The spans go to the project's destinations that take `traces`. 0 (the default): none, and the platform takes no part in a request's trace: its `traceparent` and `tracestate` reach the instance as they came. Above 0: every request's instance is given a `traceparent` whose parent is the platform's span, sampled or not, so that the app's own spans (its OpenTelemetry SDK's) are the children of the platform's, and an SDK that goes by its parent's flag samples what the platform samples."
          },
          "trust_incoming": {
            "type": [
              "boolean",
              "null"
            ],
            "default": false,
            "description": "Whether a request's own `traceparent` is taken. true: the platform's span is in the caller's trace, the child of the caller's span, and sampled where the caller says it is (whatever `sample_rate` is), and the caller's `tracestate` goes on with it. false (the default): every request begins a trace of its own, sampled at `sample_rate`, and its span links to the caller's; the caller's `tracestate` does not reach the instance. Taken, a caller decides what is sampled, and so what is sent to the destinations: for a service open to anybody, leave it false (W3C Trace Context, section 8.1)."
          }
        }
      },
      "ServiceDriveGiven": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "drive",
          "path"
        ],
        "properties": {
          "drive": {
            "type": "string",
            "maxLength": 63,
            "description": "A Drive of the project's (`POST /v1/volumes`), by its name or its id: not a key's (`key_drives`)."
          },
          "path": {
            "type": "string",
            "pattern": "^/.",
            "maxLength": 4096,
            "description": "Where it is mounted in the instance: not the root."
          },
          "mode": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "read_write",
              "snapshot",
              null
            ],
            "description": "`read_write`, the default: the one writer. `snapshot`, a read-only mount of a snapshot of the Drive that many instances may have, is a sandbox's (slice D7): an app's version mounts none yet, and is refused (400 `invalid_field`)."
          }
        }
      },
      "KeyDriveGiven": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "name",
          "path"
        ],
        "properties": {
          "name": {
            "type": "string",
            "maxLength": 63,
            "description": "Its name among the key's Drives: 1 to 63 lower-case letters, digits and hyphens, not beginning or ending with a hyphen. A later version that names it again mounts the same Drive."
          },
          "path": {
            "type": "string",
            "pattern": "^/.",
            "maxLength": 4096,
            "description": "Where it is mounted in the key's instance: not the root."
          },
          "size_bytes": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int64",
            "minimum": 16777216,
            "description": "The most it holds: a whole number of 4 KiB blocks (a multiple of 4096), at least 16 MiB, at most what the plan of the account lets one Drive be (403 `drive_limit_reached` when a key's first instance is made). Not given: 64 GiB, or 5 GiB on Free (an even share of its 15 GiB together, so that three of no size fit). Only what is written is billed."
          },
          "nocopy": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "`true`: a key's new Drive is mounted empty. Not given, or `false`: it is given what the image has at `path` when it is first mounted, as Docker gives a new named volume."
          }
        }
      },
      "ServiceVolumeGiven": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "volume",
          "path"
        ],
        "properties": {
          "volume": {
            "type": "string",
            "maxLength": 63,
            "description": "A volume of the project's, by its name or its id."
          },
          "path": {
            "type": "string",
            "pattern": "^/.",
            "maxLength": 4096,
            "description": "Where it is mounted in an instance: not the root."
          },
          "read_only": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "Not given: `false`."
          },
          "nocopy": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "`true`: a volume of the service's own that is new is mounted empty. Not given, or `false`: it is given what the image has at `path` when it is first mounted, with the owners and modes the image has there, as Docker gives a new named volume (Compose's `nocopy`). Of no effect for a shared volume, which is mounted as it is."
          }
        }
      },
      "ServiceDependencyGiven": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "service"
        ],
        "properties": {
          "service": {
            "type": "string",
            "description": "Another service of the project, by its name."
          },
          "condition": {
            "anyOf": [
              {
                "type": "string",
                "enum": [
                  "started",
                  "ready",
                  "completed"
                ]
              },
              {
                "type": "null"
              }
            ],
            "description": "What is waited for: `started`; `ready`, its readiness check passed; `completed`, it is a job that ran to its end. Not given: `started`."
          }
        }
      },
      "ServiceNetworkGiven": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "aliases": {
            "type": [
              "array",
              "null"
            ],
            "maxItems": 32,
            "items": {
              "type": "string",
              "maxLength": 253,
              "pattern": "^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?(\\.[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?)*$"
            },
            "description": "Other names: host names of lower-case labels, which may have dots (`pg.example.test`); not `localhost` or `internal`, nothing under either, not beginning with `sbx-`; each once, and none of them another app's name or alias (409 `name_taken`). `null`: none."
          },
          "endpoint_mode": {
            "anyOf": [
              {
                "type": "string",
                "enum": [
                  "vip",
                  "dnsrr"
                ]
              },
              {
                "type": "null"
              }
            ],
            "description": "`vip`: its names answer its address. `dnsrr`: they answer the addresses of its instances. `null`: `vip`."
          }
        }
      },
      "ServiceFileGiven": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "path"
        ],
        "properties": {
          "path": {
            "type": "string",
            "pattern": "^/",
            "maxLength": 4096,
            "description": "Its path in an instance, not ending in `/`."
          },
          "content": {
            "type": [
              "string",
              "null"
            ],
            "description": "Its text, without NUL. Either this or `secret`."
          },
          "secret": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 128,
            "description": "The name of one of the project's secrets, whose value it holds. Either this or `content`."
          },
          "mode": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "maximum": 4095,
            "description": "Not given: 0644, and 0400 for a secret."
          },
          "uid": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "maximum": 4294967295,
            "description": "Not given: 0."
          },
          "gid": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "maximum": 4294967295,
            "description": "Not given: 0."
          }
        }
      },
      "ServiceStopGiven": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "signal": {
            "type": [
              "string",
              "null"
            ],
            "description": "The signal it is sent first: SIGTERM, SIGINT, SIGQUIT, SIGHUP, SIGKILL, SIGUSR1, SIGUSR2, SIGWINCH or SIGPWR, with or without `SIG`. `null`: the image's own, else SIGTERM."
          },
          "grace_ms": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "maximum": 600000,
            "description": "How long it is given before it is killed. `null`: ten seconds."
          }
        }
      },
      "RuntimeGiven": {
        "type": "object",
        "description": "A service's runtime settings, as a Compose file's `ulimits`, `sysctls`, `tmpfs`, `dns` and `shm_size` ask for them: the guest's PID 1 applies them as the instance's machine boots, and the limits in the start command and in every command run in the instance (as `docker exec`). Each part is held to what the guest takes, and one it does not is refused naming it (`invalid_field`, `unknown_field`). A node from before runtime settings does not build a version that has any: its deploy fails `runtime_not_supported`.",
        "additionalProperties": false,
        "properties": {
          "ulimits": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": {
              "anyOf": [
                {
                  "type": "integer",
                  "minimum": -1
                },
                {
                  "$ref": "#/components/schemas/RuntimeLimitGiven"
                }
              ]
            },
            "description": "Limits by Docker's names (core, cpu, data, fsize, locks, memlock, msgqueue, nice, nofile, nproc, rss, rtprio, rttime, sigpending, stack): a number for both the soft and the hard limit, or `{soft, hard}`; -1 is no limit. Open files (`nofile`) from 1 to 1048576, and never -1. Without one: open files 1024, raised to 524288 at the most, as under Docker."
          },
          "sysctls": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": {
              "type": [
                "string",
                "integer"
              ]
            },
            "maxProperties": 64,
            "description": "Settings of the guest's kernel by name, those Docker lets a container set: `net.*`, `kernel.shm*`, `kernel.msg*`, `kernel.sem`, `fs.mqueue.*`. A value of 1 to 1024 bytes on one line. One the guest's kernel does not have fails the build, saying which."
          },
          "tmpfs": {
            "type": [
              "array",
              "null"
            ],
            "maxItems": 16,
            "items": {
              "$ref": "#/components/schemas/RuntimeTmpfsGiven"
            },
            "description": "File systems in the instance's memory, mounted as it boots."
          },
          "dns": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/RuntimeDnsGiven"
              },
              {
                "type": "null"
              }
            ]
          },
          "shm_size_bytes": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 1048576,
            "maximum": 1099511627776,
            "description": "The size of /dev/shm. `null`: half of the instance's memory."
          }
        }
      },
      "RuntimeTmpfsGiven": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "path"
        ],
        "properties": {
          "path": {
            "type": "string",
            "maxLength": 1024,
            "description": "From the root, none of the machine's own file systems (/proc, /sys, /dev, /run: /run is a tmpfs already, /dev/shm's size is `shm_size_bytes`) or the platform's (/sbx, /etc/sbx, /run/sbx), and no volume's path."
          },
          "size_bytes": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 1048576,
            "maximum": 1099511627776,
            "description": "`null`: half of the instance's memory. What is written there is the instance's memory."
          },
          "mode": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "maximum": 4095,
            "description": "Its bits. `null`: 0o1777 (1023)."
          }
        }
      },
      "RuntimeDnsGiven": {
        "type": "object",
        "additionalProperties": false,
        "description": "The instance's resolver, /etc/resolv.conf, in the place of the image's own. On its project's network (every service's instance), and where its egress rules can refuse a name, the node answers every question in the place of the server it is sent to: the project's names first, then the internet's through the node's resolvers, as the egress rules allow. `search` and `options` work as written.",
        "properties": {
          "servers": {
            "type": [
              "array",
              "null"
            ],
            "maxItems": 3,
            "items": {
              "type": "string",
              "description": "An IPv4 or IPv6 address."
            }
          },
          "search": {
            "type": [
              "array",
              "null"
            ],
            "maxItems": 6,
            "items": {
              "type": "string",
              "maxLength": 253
            }
          },
          "options": {
            "type": [
              "array",
              "null"
            ],
            "maxItems": 16,
            "items": {
              "type": "string",
              "maxLength": 64,
              "description": "Such as `ndots:1` or `use-vc`."
            }
          }
        }
      },
      "AppList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "data",
          "next_cursor"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/App"
            }
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "Balance": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "account_id",
          "period",
          "credits",
          "owed",
          "used_up",
          "stopped",
          "lots",
          "auto_reload",
          "reload_below_credits",
          "reload_amount_usd",
          "reloaded_usd",
          "spend_limit_usd",
          "reload_left_usd",
          "card",
          "charges",
          "ledger"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "balance"
          },
          "account_id": {
            "type": "string"
          },
          "period": {
            "type": "string",
            "description": "The month it is, `YYYY-MM` (UTC): the month `reloaded_usd` is of."
          },
          "credits": {
            "type": "number",
            "description": "What the account holds: the sum of its ledger's entries, with at most six digits after the point. It pays for what is used past what the plan includes. Below zero when it owes."
          },
          "owed": {
            "type": "number",
            "description": "What the account owes: what it kept (sleeping sandboxes, volumes, snapshots, backups, buckets) past the storage its plan includes that none of its Credits has paid. The first thing the next Credits pay, the earliest month first. From `--debt-charge-least` ($0.50) it is a debt (`GET /v1/billing`, `debt`); less is carried by the next charge of the account's subscription (`Charge.debt_usd`)."
          },
          "used_up": {
            "type": "boolean",
            "description": "Something of the account is stopped because what its plan includes of a meter is used up and it holds nothing (`stopped` says which)."
          },
          "stopped": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "cpu",
                "memory",
                "requests",
                "egress",
                "logs"
              ],
              "x-extensible-enum": true
            },
            "description": "The meters of which what the plan includes is used up while the account holds nothing: what each serves is stopped (`BillingMeter.stopped`). Empty: nothing is."
          },
          "lots": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Lot"
            },
            "description": "The lots that have not lapsed and of which something is left, the one that lapses first first: the order they pay in. What is left of them adds up to `credits` when nothing is owed (what is left of a lot that has just lapsed is in `credits` for the seconds until it is written off)."
          },
          "auto_reload": {
            "type": "boolean",
            "description": "Whether the balance is topped up when it is below the line, from the account's card (`card`). Off until the account turns it on (an account that chose nothing has it as the control plane was started: off by default), on any plan, whether it ever topped up or not."
          },
          "reload_below_credits": {
            "type": "number",
            "description": "The line: a balance below it is topped up."
          },
          "reload_amount_usd": {
            "type": "number",
            "description": "The amount of a top-up that auto reload makes."
          },
          "reloaded_usd": {
            "type": "number",
            "description": "What auto reload has taken in the month: its top-ups of the month that were collected or are to collect."
          },
          "spend_limit_usd": {
            "type": [
              "number",
              "null"
            ],
            "description": "The account's spend limit (`/v1/billing`): the most the control plane takes from the card by itself in a month, by auto reload and on the billing day for what is owed; null: none."
          },
          "reload_left_usd": {
            "type": [
              "number",
              "null"
            ],
            "description": "What auto reload may take still this month: what the spend limit leaves it (the limit, less what auto reload took, `reloaded_usd`, and what was owed, charged on the billing day), and no more than the amounts of the account's trust level in force leave it (`GET /v1/billing`, `trust`: what may be taken by auto reload, and by top-ups and auto reloads together, in a month), the smaller of the two; null when neither has a limit. A spend limit the account set stays as it set it when its level is lowered: the level holds every charge as it is made. A top-up is made only if its whole amount is left."
          },
          "card": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/CardOnRecord"
              },
              {
                "type": "null"
              }
            ],
            "description": "The account's card on record: what auto reload, and what it owes on its billing day, are charged to. Where Stripe collects (`--collector stripe`), the card of its customer, which a payment page saves (`POST /v1/billing/checkout`); null: it has none, and auto reload cannot be turned on. Where what stands in for a collector collects, every account has one, of which nothing is known."
          },
          "charges": {
            "type": "string",
            "description": "Where the account's top-ups are."
          },
          "ledger": {
            "type": "string",
            "description": "Where the entries are that the balance is the sum of."
          }
        }
      },
      "CardOnRecord": {
        "type": "object",
        "additionalProperties": false,
        "description": "A card on record, as far as it is known: what a payment page recorded of it. Of one the operator's SetupIntent bound, and of what stands in for a card where nothing collects but the stand-in, all four are null.",
        "required": [
          "brand",
          "last4",
          "exp_month",
          "exp_year"
        ],
        "properties": {
          "brand": {
            "type": [
              "string",
              "null"
            ],
            "description": "As Stripe names it: `visa`, `mastercard`, `amex`, ..."
          },
          "last4": {
            "type": [
              "string",
              "null"
            ]
          },
          "exp_month": {
            "type": [
              "integer",
              "null"
            ]
          },
          "exp_year": {
            "type": [
              "integer",
              "null"
            ]
          }
        }
      },
      "BalanceUpdate": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "auto_reload": {
            "type": "boolean",
            "description": "`true` takes a card on record (`Balance.card`): 409 `no_card` without one. An account that never chose a spend limit is given its plan's when it turns auto reload on (`spend_limit_usd` of the plan's flag: $50 on Paid as the control plane is started by default; Free gives none, and an account on it is given what its trust level lets auto reload take in a month; no more than its trust level lets)."
          },
          "reload_below_credits": {
            "type": "number",
            "minimum": 0,
            "maximum": 1000000,
            "description": "Credits, held to the millionth."
          },
          "reload_amount_usd": {
            "type": "number",
            "description": "US dollars in whole cents, no less than the smallest top-up ($20 as the control plane is started by default) and no more than $1,000,000."
          },
          "start_now": {
            "type": "boolean",
            "description": "With `auto_reload` `true`: the request each auto reload carries while auto reload stays on (turned off, it is forgotten). `true`: the account asks for what it buys to be provided at once, within the 14 days in which a consumer in the European Economic Area or the United Kingdom may withdraw from it, and acknowledges that it pays for what was provided if it withdraws and that a top-up whose Credits are all used has been provided in full and can no longer be withdrawn from (`GET /v1/billing/withdrawals`, `right.start_now`, has the words). Required of an account that buys as a consumer there (no tax id that is verified or being verified, and its billing address there, or, without one, the address the call comes from): 400 `start_now_required` without it. Kept with the purchase wherever it is given: without it, a withdrawal gives back all that was paid (control/README.md, 撤回)."
          }
        }
      },
      "AppUpdate": {
        "description": "The fields of `AppCreate`, as a JSON Merge Patch: an object is merged into what there is (`env`, `secrets`, `metadata` and `egress` key by key, `null` taking a key away; `scale`, `size` and `logs` part by part), and `null` for a field takes it back to what it is when nothing is said. `image` and `name` are replaced, never taken away.",
        "$ref": "#/components/schemas/AppCreate"
      },
      "AppChanged": {
        "type": "object",
        "description": "The answer to a change of an app: the app after the change, and which version the change made.",
        "additionalProperties": false,
        "required": [
          "id",
          "object",
          "name",
          "image",
          "image_pull_secret",
          "start_command",
          "start_args",
          "port",
          "ready",
          "env",
          "secrets",
          "size",
          "regions",
          "scale",
          "keys",
          "egress",
          "metadata",
          "logs",
          "traces",
          "public",
          "volumes",
          "drives",
          "key_drives",
          "depends_on",
          "network",
          "kind",
          "files",
          "user",
          "working_dir",
          "stop",
          "hostname",
          "runtime",
          "private",
          "url",
          "direct",
          "direct_url",
          "checkpoint",
          "current_version",
          "deploying",
          "build_failures",
          "logs_state",
          "created_at",
          "updated_at",
          "made_version",
          "managed_by"
        ],
        "properties": {
          "managed_by": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": false,
            "required": [
              "object",
              "id"
            ],
            "properties": {
              "object": {
                "type": "string",
                "enum": [
                  "database",
                  "sandboxes",
                  "installation"
                ]
              },
              "id": {
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "description": "What changes it alone, where something does. `object` `database`: the database it is under (`id`, `/v1/databases`); every other call that would change it is refused, 409 `managed_by_database`. `object` `sandboxes` (`id` null): the Sandboxes product's, made by the creates of the project's sandboxes (`POST /v1/sandboxes`): the project's default sandbox app, `sbx-sandbox` (its `image` null: each sandbox is made from the base template of the node it goes to, until the project names an image for it, `PATCH` with `image` alone, a new version made of that image, of which the creates that name no image are made from then on), or the app of an image a create named (`image`, as the create named it; `sbx-<the image's name>-<12 hex digits>`, one version). A version is ready from when it is made, and the app's instances are the project's sandboxes; every call of it but reading it and its versions, naming the default sandbox app's image and deleting it (its versions' templates go with it; the next create of its image, or of none, makes it again) is refused, 409 `managed_by_sandboxes`. Null: none's. `object` `installation`: the installation of a Managed Application it is part of (`id`, `/v1/installations`), which changes it alone: every other call that would change it is refused, 409 `managed_by_installation`."
          },
          "public": {
            "$ref": "#/components/schemas/App/properties/public"
          },
          "volumes": {
            "$ref": "#/components/schemas/App/properties/volumes"
          },
          "drives": {
            "$ref": "#/components/schemas/App/properties/drives"
          },
          "key_drives": {
            "$ref": "#/components/schemas/App/properties/key_drives"
          },
          "depends_on": {
            "$ref": "#/components/schemas/App/properties/depends_on"
          },
          "network": {
            "$ref": "#/components/schemas/App/properties/network"
          },
          "kind": {
            "$ref": "#/components/schemas/App/properties/kind"
          },
          "files": {
            "$ref": "#/components/schemas/App/properties/files"
          },
          "user": {
            "$ref": "#/components/schemas/App/properties/user"
          },
          "working_dir": {
            "$ref": "#/components/schemas/App/properties/working_dir"
          },
          "stop": {
            "$ref": "#/components/schemas/App/properties/stop"
          },
          "hostname": {
            "$ref": "#/components/schemas/App/properties/hostname"
          },
          "runtime": {
            "$ref": "#/components/schemas/App/properties/runtime"
          },
          "private": {
            "$ref": "#/components/schemas/App/properties/private"
          },
          "id": {
            "$ref": "#/components/schemas/App/properties/id"
          },
          "object": {
            "$ref": "#/components/schemas/App/properties/object"
          },
          "name": {
            "$ref": "#/components/schemas/App/properties/name"
          },
          "image": {
            "$ref": "#/components/schemas/App/properties/image"
          },
          "image_pull_secret": {
            "$ref": "#/components/schemas/App/properties/image_pull_secret"
          },
          "start_command": {
            "$ref": "#/components/schemas/App/properties/start_command"
          },
          "start_args": {
            "$ref": "#/components/schemas/App/properties/start_args"
          },
          "port": {
            "$ref": "#/components/schemas/App/properties/port"
          },
          "ready": {
            "$ref": "#/components/schemas/App/properties/ready"
          },
          "env": {
            "$ref": "#/components/schemas/App/properties/env"
          },
          "secrets": {
            "$ref": "#/components/schemas/App/properties/secrets"
          },
          "size": {
            "$ref": "#/components/schemas/App/properties/size"
          },
          "regions": {
            "$ref": "#/components/schemas/App/properties/regions"
          },
          "scale": {
            "$ref": "#/components/schemas/App/properties/scale"
          },
          "keys": {
            "$ref": "#/components/schemas/App/properties/keys"
          },
          "egress": {
            "$ref": "#/components/schemas/EgressRules",
            "description": "Egress rules as a sandbox has them (`default`, `allow`, `deny`, `http`, `inject`), null when there are none; kept as the calls gave them (a `PATCH` merges them key by key, as a node does). They hold at once and make no version: the nodes that serve the app put them on every instance of its group, the ones there are (one that runs for the connections it has too) and the ones made after; a keyed instance does not have them. The control plane checks only that they are an object of those keys of at most 256 KiB: what the rules say is a node's to check, when it applies them, and an instance whose rules its node cannot read is refused everything (the node's log says why)."
          },
          "metadata": {
            "$ref": "#/components/schemas/App/properties/metadata"
          },
          "logs": {
            "$ref": "#/components/schemas/App/properties/logs"
          },
          "traces": {
            "$ref": "#/components/schemas/App/properties/traces"
          },
          "url": {
            "$ref": "#/components/schemas/App/properties/url"
          },
          "direct": {
            "$ref": "#/components/schemas/App/properties/direct"
          },
          "checkpoint": {
            "$ref": "#/components/schemas/App/properties/checkpoint"
          },
          "direct_url": {
            "$ref": "#/components/schemas/App/properties/direct_url"
          },
          "current_version": {
            "$ref": "#/components/schemas/App/properties/current_version"
          },
          "deploying": {
            "$ref": "#/components/schemas/App/properties/deploying"
          },
          "build_failures": {
            "$ref": "#/components/schemas/App/properties/build_failures"
          },
          "logs_state": {
            "$ref": "#/components/schemas/App/properties/logs_state"
          },
          "created_at": {
            "$ref": "#/components/schemas/App/properties/created_at"
          },
          "updated_at": {
            "$ref": "#/components/schemas/App/properties/updated_at"
          },
          "made_version": {
            "type": [
              "string",
              "null"
            ],
            "description": "The id of the version this call made: a change of what a version is made of, of an app that has an image, makes one, and it is the one the app is `deploying` when the call answers. `null` when the call made none (it changed what holds at once, or nothing). `deploying` alone does not say that: it may be a version another call made."
          }
        }
      },
      "Deploy": {
        "type": "object",
        "description": "What the version is made of, where that is not what the app has. Every field may be left out, and `null` is as if the field were not given.",
        "additionalProperties": false,
        "properties": {
          "image": {
            "type": [
              "string",
              "null"
            ]
          },
          "image_pull_secret": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 128,
            "description": "In place of the app's."
          },
          "start_command": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/StartCommand"
              },
              {
                "type": "null"
              }
            ]
          },
          "start_args": {
            "anyOf": [
              {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "minItems": 1,
                "maxItems": 1000
              },
              {
                "type": "null"
              }
            ]
          },
          "port": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 1,
            "maximum": 65535
          },
          "ready": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Ready"
              },
              {
                "type": "null"
              }
            ]
          },
          "env": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": {
              "type": "string"
            },
            "description": "In place of the app's, not merged into it."
          },
          "secrets": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": {
              "type": "string"
            },
            "description": "In place of the app's, not merged into it."
          },
          "size": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Size"
              },
              {
                "type": "null"
              }
            ]
          },
          "volumes": {
            "$ref": "#/components/schemas/AppCreate/properties/volumes"
          },
          "drives": {
            "$ref": "#/components/schemas/AppCreate/properties/drives"
          },
          "key_drives": {
            "$ref": "#/components/schemas/AppCreate/properties/key_drives"
          },
          "kind": {
            "$ref": "#/components/schemas/AppCreate/properties/kind"
          },
          "files": {
            "$ref": "#/components/schemas/AppCreate/properties/files"
          },
          "user": {
            "$ref": "#/components/schemas/AppCreate/properties/user"
          },
          "working_dir": {
            "$ref": "#/components/schemas/AppCreate/properties/working_dir"
          },
          "stop": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/ServiceStopGiven"
              },
              {
                "type": "null"
              }
            ],
            "description": "In place of the app's, not merged into it."
          },
          "hostname": {
            "$ref": "#/components/schemas/AppCreate/properties/hostname"
          },
          "runtime": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/RuntimeGiven"
              },
              {
                "type": "null"
              }
            ],
            "description": "In place of the app's."
          }
        }
      },
      "Version": {
        "type": "object",
        "description": "What one deploy made of an app. It never changes but for how its deploy went.",
        "additionalProperties": false,
        "required": [
          "id",
          "object",
          "app_id",
          "number",
          "status",
          "image",
          "image_pull_secret",
          "start_command",
          "start_args",
          "port",
          "ready",
          "env",
          "secrets",
          "size",
          "volumes",
          "drives",
          "key_drives",
          "kind",
          "files",
          "user",
          "working_dir",
          "stop",
          "hostname",
          "runtime",
          "error",
          "resolved",
          "digest",
          "template",
          "created_at",
          "finished_at",
          "collected_at",
          "build"
        ],
        "properties": {
          "volumes": {
            "$ref": "#/components/schemas/App/properties/volumes"
          },
          "drives": {
            "$ref": "#/components/schemas/App/properties/drives"
          },
          "key_drives": {
            "$ref": "#/components/schemas/App/properties/key_drives"
          },
          "kind": {
            "$ref": "#/components/schemas/App/properties/kind"
          },
          "files": {
            "$ref": "#/components/schemas/App/properties/files"
          },
          "user": {
            "$ref": "#/components/schemas/App/properties/user"
          },
          "working_dir": {
            "$ref": "#/components/schemas/App/properties/working_dir"
          },
          "stop": {
            "$ref": "#/components/schemas/App/properties/stop"
          },
          "hostname": {
            "$ref": "#/components/schemas/App/properties/hostname"
          },
          "runtime": {
            "$ref": "#/components/schemas/App/properties/runtime"
          },
          "id": {
            "type": "string",
            "pattern": "^ver-[0-9a-hjkmnp-tv-z]{16}$"
          },
          "object": {
            "type": "string",
            "enum": [
              "version"
            ]
          },
          "app_id": {
            "type": "string"
          },
          "number": {
            "type": "integer",
            "minimum": 1,
            "description": "1 for an app's first version, and one more for each after it."
          },
          "status": {
            "type": "string",
            "x-extensible-enum": [
              "building",
              "ready",
              "failed"
            ],
            "description": "`building`: its template is being built on the app's nodes. `ready`: every one of them has it; it took the app's traffic then, unless a newer deploy or a rollback had taken the app away from it. `failed`: `error` says why, and the traffic stayed where it was. A version that is ready is `building` again while a rollback to it has it built again (its template had been taken off the nodes: `collected_at`), and `ready` or `failed` after, by how that went."
          },
          "image": {
            "type": "string",
            "description": "As it was given. `resolved.image` is the image that was deployed."
          },
          "image_pull_secret": {
            "type": [
              "string",
              "null"
            ],
            "description": "The name of the secret its image is pulled with. What a node pulls with is the secret's value when that node is asked to build the version. `null`: none."
          },
          "start_command": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/StartCommand"
              },
              {
                "type": "null"
              }
            ]
          },
          "start_args": {
            "$ref": "#/components/schemas/App/properties/start_args"
          },
          "port": {
            "type": [
              "integer",
              "null"
            ],
            "description": "As it was given. `resolved.port` is the port the instances are asked on."
          },
          "ready": {
            "$ref": "#/components/schemas/Ready"
          },
          "env": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "The values: each reference of the app's `env` is what it came to when the version was made, and a variable that referred to a secret is in `secrets`."
          },
          "secrets": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "Names of secrets: the app's, and those its references led to. The values its instances have are the secrets' values when its template was built."
          },
          "size": {
            "allOf": [
              {
                "$ref": "#/components/schemas/Size"
              }
            ],
            "required": [
              "vcpus",
              "memory_bytes"
            ]
          },
          "error": {
            "type": [
              "object",
              "null"
            ],
            "description": "Why it failed. The codes are a node's for a sandbox that fails the same way (`image_not_found`: the image is not in its registry, or is not public, which is all a registry that wants a login says to a version that names no `image_pull_secret`; `image_pull_failed`; `registry_auth_failed`: the registry refused the credentials of `image_pull_secret`, and `message` ends with what the registry said, without them; `image_unpack_failed`, `invalid_image`, `boot_failed`: the start command did not come up, and `message` has what the ready check got and what the start command printed last, in the image's own words), a node's refusal of what the version is made of (`invalid_field` and the like), or the control plane's own: `secret_not_found` and `secret_unreadable` (a secret the version names is gone, or cannot be read, when a node is asked to build it; `message` names it), `invalid_field` (the secret of `image_pull_secret` no longer holds a user name and a password), `secret_host_not_allowed` (the secret of `image_pull_secret` may no longer go to the image's registry; `message` names it, the registry and where it may go), `no_capacity` (no machine was there to build it on, or the node had no slot to), `drive_full` (no machine that could build it had room on its data drive for its template: `message` is what the first of them said, how full its drive is; where another had room it was built there), `build_failed` (a node failed at something of its own), a node's own code where it failed so at the question what the image is, or at the build, three times (`internal`: `message` has its words, the status it answered and the id of the request, which its log has the reason under), `template_differs`, `runtime_not_supported` (a node that is to serve it runs a sandboxd from before runtime settings, and builds nothing without the version's `runtime`), `deploy_stalled` (a machine that is to build it took nothing of it for two minutes: `message` has what it said), `deploy_timed_out`.",
            "additionalProperties": false,
            "required": [
              "code",
              "message"
            ],
            "properties": {
              "code": {
                "type": "string"
              },
              "message": {
                "type": "string"
              }
            }
          },
          "resolved": {
            "type": [
              "object",
              "null"
            ],
            "description": "What the deploy found of the image, once, before anything was built: every node builds by it, now and whenever the version is built again, so a tag that is moved afterwards changes no version there is. `null` until then.",
            "additionalProperties": false,
            "required": [
              "image",
              "port",
              "unmounted_volumes"
            ],
            "properties": {
              "image": {
                "type": "string",
                "description": "The image by its digest: `ghcr.io/acme/api@sha256:...`."
              },
              "port": {
                "type": "integer",
                "description": "The port the instances are asked on: `port`, else the one port the image declares, else 8080."
              },
              "unmounted_volumes": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "The paths the image declares as volumes (`VOLUME`) that no volume or Drive of the version is mounted at (or above). Docker mounts a volume of its own at such a path; here it is a directory of each instance's own disk, which a new version does not keep. `glac` says so once the version is deployed. Empty for a version resolved before the paths were looked at."
              }
            }
          },
          "digest": {
            "type": [
              "string",
              "null"
            ],
            "description": "The image it is, by its digest (`sha256:...`). Of a deployed version, the one its deploy resolved (the digest of `resolved.image`), null until then. Of a version of an app of the Sandboxes product (`managed_by.object` `sandboxes`): a naming of the app's image is the image its tag named when the first sandbox of it was made; a tag that is moved later makes another version of the app, under that naming, at the first sandbox of the image it names then. Null until a sandbox of it says, and for the base template's."
          },
          "template": {
            "type": [
              "string",
              "null"
            ],
            "description": "The key of the version's template, the same on every node: how nodes know the version, and what its instances are labelled with (`template`). No other version has it. `null` until a node was asked to build it."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "finished_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When its build ended, well or not; for a version that a rollback had built again, when that build did. `null` while it is building."
          },
          "collected_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the version's template was taken off the nodes: the nodes keep the templates of the version that has the app's traffic and of the app's newest versions that are ready (three, the one that has the traffic among them as a rule), and of no other. The version is what it was otherwise, and a rollback to it builds it again first. `null` while the nodes keep its template, and again once it has been built again."
          },
          "build": {
            "type": [
              "object",
              "null"
            ],
            "description": "How far the nodes of the version's deploy are with it, as the deploy saw them last: it is kept when the deploy has ended, and is that deploy's from then on (a node that builds the version later, because it was away or is new, changes nothing here). Numbers and a node's words, and no node's name. `null` until the deploy has picked the version's nodes (its image is found first), for a version from before deploys kept this, and from the moment a rollback has the version built again until that deploy has picked its nodes.",
            "additionalProperties": false,
            "required": [
              "nodes",
              "ready",
              "failures"
            ],
            "properties": {
              "nodes": {
                "type": "integer",
                "minimum": 0,
                "description": "On how many nodes the version is built: the nodes that are to serve it. A node with no room on its data drive for the template (`drive_full`) is none of them where another node of its region has room, which is one of them in its place."
              },
              "ready": {
                "type": "integer",
                "minimum": 0,
                "description": "How many of them have its template. The version is `ready` when all of them do and the app's traffic has been given to it, a moment after the last one."
              },
              "failures": {
                "type": "array",
                "description": "One for each of those nodes whose last try at building the version failed, with the node's code and words as in the version's `error`; a node that is trying again is among them until it has built the version. While the version is `building` these are troubles of a node's own (`no_capacity`, `build_failed`, `unavailable`, `internal`), and the node is asked again, three times at the most; and what a node said that has not taken the build (it refused it for now, or could not be asked), which it is asked again for, two minutes at the most (`deploy_stalled`). On a version that `failed` because a node could not build it, the one that failed it is here as it is in `error`.",
                "items": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "code",
                    "message"
                  ],
                  "properties": {
                    "code": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      },
      "VersionList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "data",
          "next_cursor"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Version"
            }
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "Rollback": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "version"
        ],
        "properties": {
          "version": {
            "type": [
              "string",
              "integer"
            ],
            "description": "One of the app's versions that is `ready`: its id, or its number."
          }
        }
      },
      "LogLine": {
        "description": "One line of the answer of `GET /v1/apps/{app}/logs`: a line of the app's log, of one of its three kinds, or a mark.",
        "oneOf": [
          {
            "$ref": "#/components/schemas/LogOutput"
          },
          {
            "$ref": "#/components/schemas/LogRequest"
          },
          {
            "$ref": "#/components/schemas/LogPlatform"
          },
          {
            "$ref": "#/components/schemas/LogMark"
          }
        ]
      },
      "LogOutput": {
        "type": "object",
        "description": "A line that an instance's start command printed.",
        "additionalProperties": false,
        "required": [
          "time",
          "kind",
          "instance",
          "version",
          "stream",
          "text",
          "cursor"
        ],
        "properties": {
          "time": {
            "type": "string",
            "format": "date-time",
            "description": "When it was printed, in UTC, to the microsecond: by the instance's own clock, which its machine sets when the instance is made and when it wakes, held to the time between the machine's last two readings of the instance."
          },
          "kind": {
            "type": "string",
            "enum": [
              "output"
            ]
          },
          "instance": {
            "type": [
              "string",
              "null"
            ],
            "description": "The instance that printed it, by its id. Null for a line the start command printed while a machine built the version's template (`from`)."
          },
          "version": {
            "type": [
              "string",
              "null"
            ],
            "description": "The id of the instance's version."
          },
          "stream": {
            "type": "string",
            "enum": [
              "stdout",
              "stderr"
            ]
          },
          "text": {
            "type": "string",
            "description": "The line, without the line feed that ended it and without a carriage return before that. Bytes that are no UTF-8 are replaced (U+FFFD). A line that has not ended is given as far as it came when its last byte is a second old, or its instance is stopped or deleted."
          },
          "cut_bytes": {
            "type": "integer",
            "minimum": 1,
            "description": "The line was longer than a machine keeps of one (16,384 bytes unless the machine was told otherwise): this many bytes were cut off its end."
          },
          "from": {
            "type": "string",
            "enum": [
              "template"
            ],
            "description": "The line was printed while a machine built the version's template, before the template was saved: every instance on that machine has it behind it, and it is no instance's. Once for each machine that built the template."
          },
          "cursor": {
            "type": "string",
            "description": "Where a reader stands after this line."
          }
        }
      },
      "LogRequest": {
        "type": "object",
        "description": "A request for the app that reached one of its machines: written when its answer has ended, or, for those the machine refused, when the second they came in is over: one line a second for each version and error code, which says how many it stands for (`times`). Where the app keeps a share of its requests (`logs.requests.sample_rate`), those its sample keeps, each saying the share. A request that an edge answered itself, without giving it to a machine, has no line: it is counted in `edge_refused` of `GET /v1/apps/{app}/logs/usage`. One that an edge gave to a second machine after the first was full has two: the first machine's, refused there with `app_full_here`, and the second's.",
        "additionalProperties": false,
        "required": [
          "time",
          "kind",
          "instance",
          "version",
          "request_id",
          "method",
          "path",
          "status",
          "duration_ms",
          "bytes",
          "cursor"
        ],
        "properties": {
          "time": {
            "type": "string",
            "format": "date-time",
            "description": "When the last byte of the answer came from the instance, or when the machine refused the request."
          },
          "kind": {
            "type": "string",
            "enum": [
              "request"
            ]
          },
          "instance": {
            "type": [
              "string",
              "null"
            ],
            "description": "The instance the request was given to. Null: it was given to none (`refused`)."
          },
          "version": {
            "type": [
              "string",
              "null"
            ],
            "description": "The id of the version the request was for."
          },
          "request_id": {
            "type": "string",
            "description": "The request's id: the `X-Request-Id` it came with, or one the machine made for it. The instance is given it in that header, so what it prints can name the request as this line does."
          },
          "method": {
            "type": "string"
          },
          "path": {
            "type": "string",
            "description": "The request's path without what follows a `?`, its first 1024 bytes."
          },
          "status": {
            "type": [
              "integer",
              "null"
            ],
            "description": "The status of the answer: the instance's, or the machine's own where it refused the request. Null: the instance closed the connection without an answer."
          },
          "duration_ms": {
            "type": "number",
            "minimum": 0,
            "description": "From when the machine had read the request's head to the last byte of the answer, in milliseconds. The time the request waited for an instance is in it."
          },
          "bytes": {
            "type": "integer",
            "minimum": 0,
            "description": "How many bytes of the answer were passed on, its head included."
          },
          "refused": {
            "type": "string",
            "description": "The machine answered the request itself, with this error code: the app was full there (`app_full_here`: the edge then tries another machine; `app_at_capacity`), or the instance could not take the request."
          },
          "instance_was": {
            "type": "string",
            "enum": [
              "paused",
              "asleep"
            ],
            "description": "The instance did not run when the request came, and was brought up for it."
          },
          "wake_ms": {
            "type": "number",
            "minimum": 0,
            "description": "With `instance_was`: how long the request waited for the instance to be brought up, in milliseconds."
          },
          "upgraded": {
            "type": "boolean",
            "enum": [
              true
            ],
            "description": "The answer was 101, and the connection was passed on as it was until it closed."
          },
          "times": {
            "type": "integer",
            "minimum": 1,
            "description": "Of a refusal: how many requests of the version the machine refused with this code in this second, the line being the first one's."
          },
          "sample_rate": {
            "type": "number",
            "minimum": 0,
            "maximum": 1,
            "description": "Where the app keeps a share of its requests below 1: the share this line stands for, which a reader counts back by. 1: the request failed, and failed ones are all kept (`keep_errors`)."
          },
          "trace_id": {
            "type": "string",
            "pattern": "^[0-9a-f]{32}$",
            "description": "The request came with a `traceparent` header the W3C's Trace Context takes: its trace's id. The record a destination of the export is sent of this line has it as its `TraceId`."
          },
          "cursor": {
            "type": "string"
          }
        }
      },
      "LogPlatform": {
        "type": "object",
        "description": "What the platform did to the app or to one of its instances on one machine, what a machine's log has to say of itself, or what this call says of the machines it reads. `event` says what, `message` says it in words, and the other fields are the event's.",
        "additionalProperties": false,
        "required": [
          "time",
          "kind",
          "instance",
          "version",
          "event",
          "message",
          "cursor"
        ],
        "properties": {
          "time": {
            "type": "string",
            "format": "date-time"
          },
          "kind": {
            "type": "string",
            "enum": [
              "platform"
            ]
          },
          "instance": {
            "type": [
              "string",
              "null"
            ],
            "description": "The instance the line is about. Null: it is about a version on a machine, or about the reading."
          },
          "version": {
            "type": [
              "string",
              "null"
            ],
            "description": "The id of the version. Null for what this call says of the machines it reads."
          },
          "event": {
            "type": "string",
            "x-extensible-enum": [
              "template.built",
              "version.served",
              "version.removed",
              "instance.made",
              "instance.stopped",
              "instance.woken",
              "instance.retired",
              "instance.deleted",
              "output.dropped",
              "output.lost",
              "output.unavailable",
              "logs.keep_off",
              "logs.keep_on",
              "logs.restarted",
              "logs.gap",
              "export.dropped",
              "export.failing",
              "export.recovered",
              "logs.incomplete",
              "logs.complete",
              "logs.anew"
            ],
            "description": "On a machine: `template.built` (the version's template was built there; the line has the time of the build), `version.served` (the machine was told to serve the version), `version.removed` (and to serve it no longer, with `reason`), `instance.made` (`took_ms`, `from_pool`, `kept`), `instance.stopped` (an idle instance was paused or put to sleep: `how`, `idle_ms`, `times`; said once a second at the most for one instance), `instance.woken` (an instance that stood still was brought up with no request for it, because the app keeps so many awake: `instance_was`, `wake_ms`; one that a request brings up has no such line, the request's line says it), `instance.retired` (the instance is given no request from here on, and goes when what it has in hand has ended: `reason`), `instance.deleted` (`reason`; the last line of an instance), `output.dropped` (the instance printed more lines within a second than a machine keeps: `lines`, `bytes`, `reason`), `output.lost` (output was gone from the instance before its machine read it: `bytes`, `reason`), `output.unavailable` (the instance is of a template from before the platform kept what a start command prints), `logs.keep_off` and `logs.keep_on` (from here on the version's output is kept on the machine and not for good, and from here on for good again: `reason`), `logs.restarted` (the machine's program before the one that wrote this ended without stopping in order: lines of requests and of the platform that it had not kept, of its last moments, are missing, and what the instances printed is read on; `last_seen`), `logs.gap` (the machine let go of lines it had not shipped to the object store, having all it keeps of one app: `reason` `node_buffer`, `from`, `to`, `lines`, `bytes`; said once a second at the most). Of this call: `logs.incomplete`, `logs.complete`, `logs.anew` (`machines_unreachable`), and `logs.gap` (lines that are missing for good: `reason` `node_gone`, `log_gone` or `unreadable`, `from`, `to`, `lines`, at the time the stretch begins). `export.dropped` (lines of the log were not exported to a destination of the project's, `/v1/destinations`: they had waited longer than the exporter's bound for it, or were older than the last of what it may hold back: `destination`, `destination_id`, `lines`, `from`, `to`, `reason`; of the app's metrics, minutes that were not, with `signal` `metrics` and `minutes` in place of `lines`). `export.failing` (a destination became `failing` on the machine for what it takes of the app, its logs or its metrics: `destination`, `destination_id`, `signal`, `since`, `error`, `next_try`; said once for each spell of failing, however often it is tried meanwhile) and `export.recovered` (it took a batch again, which ends the spell: `destination`, `destination_id`, `signal`, `since`, `lines` or `minutes` not sent to it meanwhile, `rejected`). Each of the three goes as a record to the destination it names, and to no other."
          },
          "message": {
            "type": "string",
            "description": "The event in a sentence, for a person."
          },
          "reason": {
            "type": "string",
            "x-extensible-enum": [
              "idle",
              "version_left",
              "app_deleted",
              "broken",
              "deleted",
              "rate_limit",
              "guest_buffer",
              "deleted_unread",
              "agent_restarted",
              "setting",
              "over_plan",
              "node_buffer",
              "node_gone",
              "log_gone",
              "unreadable",
              "destination_down",
              "destination_slow",
              "egress_stopped",
              "exporter_down"
            ],
            "description": "Why. `version_left`: the app's traffic has left the version, or the machine serves the app no more. `app_deleted`: the app was deleted. `broken`: the instance's port takes no connection, or the instance failed. `idle`: it was idle for longer than an instance is kept. `deleted`: by a call (`DELETE /v1/sandboxes/{id}`), or by its machine with no other reason said. Of dropped output: `rate_limit`. Of lost output: `guest_buffer` (it printed more between two readings than the instance keeps), `deleted_unread` (the instance was deleted as it stood, with what it had not handed over before it stopped), `agent_restarted` (what keeps the output in the instance began anew; how much went is not known). Of `logs.keep_off` and `logs.keep_on`: `setting` (the app's `logs.keep`), `over_plan` (the account the app is of had used up the logs its plan includes and held nothing to pay for more: the control plane's word, `logs_over_plan` of the app); of `logs.keep_on`, the reason that ended. Of `logs.gap`: `node_buffer` (the machine held all it keeps of one app, and the store could not be reached), `node_gone` (the machine is gone from the books), `log_gone` (the machine lost what it kept of the app, or keeps it anew), `unreadable` (a piece in the store could not be read). Of `export.dropped`: `destination_down` (the destination did not take them, or could not be reached), `destination_slow` (it took them more slowly than they came), `egress_stopped` (the account's traffic to the internet was stopped), `exporter_down` (no exporter ran on the machine while they were new enough), `node_buffer` (the machine let them go before the destination took them: the app had all it keeps of one app)."
          },
          "from": {
            "type": "string",
            "format": "date-time",
            "description": "`logs.gap`: the time of the earliest line that is missing, or from which on lines are. `export.dropped`: the time of the earliest line that was not exported."
          },
          "to": {
            "type": "string",
            "format": "date-time",
            "description": "`logs.gap`: the time of the latest, or up to which lines are. `export.dropped`: of the latest."
          },
          "took_ms": {
            "type": [
              "number",
              "null"
            ],
            "description": "`instance.made`: how long the making took, in milliseconds."
          },
          "from_pool": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "`instance.made`: whether the instance was one that waited ready-made."
          },
          "kept": {
            "type": "boolean",
            "description": "`instance.made`: whether it was made with no request for it, because the app keeps so many awake (`min_instances` of its `scale`). The log of such an instance begins without a request."
          },
          "how": {
            "type": "string",
            "x-extensible-enum": [
              "paused",
              "saved"
            ],
            "description": "`instance.stopped`: paused where it stood, or its memory saved and its machine stopped."
          },
          "idle_ms": {
            "type": "number",
            "description": "`instance.stopped`: how long it had been idle, in milliseconds."
          },
          "times": {
            "type": "integer",
            "minimum": 1,
            "description": "`instance.stopped`: how many stops this line stands for."
          },
          "instance_was": {
            "type": "string",
            "enum": [
              "paused",
              "asleep"
            ],
            "description": "`instance.woken`: how the instance stood when it was brought up."
          },
          "wake_ms": {
            "type": "number",
            "minimum": 0,
            "description": "`instance.woken`: how long bringing it up took, in milliseconds."
          },
          "lines": {
            "type": "integer",
            "minimum": 0,
            "description": "`output.dropped`: how many lines. `logs.gap`: how many lines are missing, of every kind; of `node_gone` and `log_gone`, at the most. `export.dropped`: how many were not exported. Of these 1 or more. `export.recovered` of logs: how many were not sent to the destination while it failed, refused by it or past the exporter's bounds; 0 or more."
          },
          "bytes": {
            "type": [
              "integer",
              "null"
            ],
            "description": "`output.dropped`: how many bytes those lines had. `output.lost`: how many bytes; null where that is not known. `logs.gap` of `node_buffer`: the bytes of the `text` of the lines of output among them."
          },
          "last_seen": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "`logs.restarted`: when the machine's program before was last seen running, to within a second; null where it left no word of that."
          },
          "machines_unreachable": {
            "type": "integer",
            "minimum": 0,
            "description": "`logs.incomplete`, `logs.complete`, `logs.anew`: how many of the machines that keep lines of the app cannot be reached now."
          },
          "destination": {
            "type": "string",
            "description": "`export.dropped`, `export.failing`, `export.recovered`: the destination's name."
          },
          "destination_id": {
            "type": "string",
            "pattern": "^dst-",
            "description": "`export.dropped`, `export.failing`, `export.recovered`: the destination's id."
          },
          "signal": {
            "type": "string",
            "enum": [
              "logs",
              "metrics",
              "traces"
            ],
            "description": "`export.dropped` of minutes of the app's metrics: `metrics`. `export.failing`, `export.recovered`: which of what the destination takes failed, its logs, its metrics or its traces."
          },
          "minutes": {
            "type": "integer",
            "minimum": 0,
            "description": "`export.dropped` of metrics: how many minutes were not exported (`from` when the first began, `to` when the last ended), 1 or more. `export.recovered` of metrics: how many were not sent to the destination while it failed, 0 or more."
          },
          "spans": {
            "type": "integer",
            "minimum": 0,
            "description": "`export.recovered` of traces: how many spans of the app's requests were not sent to the destination while it failed, 0 or more. (A span not sent has no `export.dropped` line: the request's own line is in the log.)"
          },
          "since": {
            "type": "string",
            "format": "date-time",
            "description": "`export.failing`, `export.recovered`: when the first try of the spell of failing that failed was made; the two lines of one spell say the same."
          },
          "error": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "code",
              "status",
              "message"
            ],
            "description": "`export.failing`: what was wrong when the destination became failing on that machine, as its status says it (`last_error`).",
            "properties": {
              "code": {
                "type": "string",
                "x-extensible-enum": [
                  "status",
                  "redirect",
                  "address",
                  "unreachable",
                  "no_answer"
                ],
                "description": "Why, in a word: an answer that is not sent again (`status`, `redirect`), one that is and was given for a minute (`status` of 429, 502, 503, 504), an address not on the public internet (`address`), no connection (`unreachable`) or no answer in time (`no_answer`) for a minute."
              },
              "status": {
                "type": [
                  "integer",
                  "null"
                ],
                "description": "The status of the answer; null: there was none."
              },
              "message": {
                "type": "string",
                "maxLength": 512,
                "description": "What it said, or why there was no answer, with every value of the destination's headers cut out; empty where nothing was said."
              }
            }
          },
          "next_try": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "`export.failing`: when the destination is tried next."
          },
          "rejected": {
            "type": "integer",
            "minimum": 0,
            "description": "`export.recovered`: how many of the lines (minutes, spans) not sent meanwhile the destination refused; the others waited for it longer than the exporter holds them (`export.dropped`, of lines and minutes)."
          },
          "cursor": {
            "type": "string"
          }
        }
      },
      "LogMark": {
        "type": "object",
        "description": "Where a reader stands, with no line: the last thing an answer that does not follow sends, and what one that follows sends every fifteen seconds while it has nothing else to send.",
        "additionalProperties": false,
        "required": [
          "kind",
          "cursor"
        ],
        "properties": {
          "kind": {
            "type": "string",
            "enum": [
              "mark"
            ]
          },
          "cursor": {
            "type": "string"
          },
          "more": {
            "type": "boolean",
            "const": true,
            "description": "The reading read as much of the object store as one reads, and ended here: there is more after the cursor, which the reader goes on with."
          }
        }
      },
      "AppLogDeletion": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "app",
          "before",
          "pieces",
          "bytes"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "app_log_deletion"
          },
          "app": {
            "type": "string",
            "description": "The app's id."
          },
          "before": {
            "type": "string",
            "format": "date-time",
            "description": "No line of an earlier time is read from here on: the time given, or an earlier deletion's where that was later."
          },
          "pieces": {
            "type": "integer",
            "minimum": 0,
            "description": "The pieces in the object store whose every line is before it, which go now."
          },
          "bytes": {
            "type": "integer",
            "format": "int64",
            "minimum": 0,
            "description": "What those pieces are in the store, compressed and sealed."
          }
        }
      },
      "AppLogUsage": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "app",
          "by",
          "from",
          "to",
          "data"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "app_log_usage"
          },
          "app": {
            "type": "string",
            "description": "The app's id."
          },
          "by": {
            "type": "string",
            "enum": [
              "day",
              "hour"
            ]
          },
          "from": {
            "type": "string",
            "format": "date-time",
            "description": "Where the first day (or hour) begins."
          },
          "to": {
            "type": "string",
            "format": "date-time",
            "description": "Where the last one ends."
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AppLogUsageStretch"
            }
          }
        }
      },
      "AppLogUsageStretch": {
        "type": "object",
        "description": "What an app's log counted in one day (`day`) or one hour (`hour`), every machine's added up, and what the edges refused of its requests themselves. A count a machine has that this description does not name is added up as the others are.",
        "required": [
          "complete",
          "kept_until",
          "output",
          "requests",
          "platform",
          "edge_refused",
          "exported"
        ],
        "properties": {
          "day": {
            "type": "string",
            "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}$",
            "description": "The day, by UTC, with `by=day`: `2026-10-13`."
          },
          "hour": {
            "type": "string",
            "format": "date-time",
            "description": "The hour's beginning, with `by=hour`."
          },
          "kept_until": {
            "type": "string",
            "format": "date-time",
            "description": "The lines of this day (or hour) are kept in the object store until then, by the account's plan as it is now, and go within the hour after."
          },
          "complete": {
            "type": "boolean",
            "description": "Every machine that keeps lines of the app has had its counts of this taken in after it was over (and, for the day that is not over, is there and had them taken in a moment ago). false: a machine's are missing or behind, of one that cannot be reached, or is gone."
          },
          "output": {
            "type": "object",
            "description": "What the instances printed, as the machines read it.",
            "required": [
              "printed",
              "kept",
              "not_kept",
              "cut",
              "lost"
            ],
            "properties": {
              "printed": {
                "$ref": "#/components/schemas/LinesAndBytes",
                "description": "Every line the machines read: `kept`, each of `not_kept`, and, of the bytes, `cut`."
              },
              "kept": {
                "$ref": "#/components/schemas/LinesAndBytes",
                "description": "The lines kept for good, by the bytes of their `text` as a reader of the log is given it."
              },
              "not_kept": {
                "type": "object",
                "description": "The lines that are not kept for good, by why.",
                "required": [
                  "rate_limit",
                  "keep_off",
                  "over_plan",
                  "node_buffer",
                  "node_lost"
                ],
                "properties": {
                  "rate_limit": {
                    "$ref": "#/components/schemas/LinesAndBytes",
                    "description": "Dropped: an instance printed more lines in one second than are kept of one (`output.dropped`), by the bytes they were printed as."
                  },
                  "keep_off": {
                    "$ref": "#/components/schemas/LinesAndBytes",
                    "description": "In the log on the machine, and not kept for good: the app's `logs.keep` was false at their time."
                  },
                  "over_plan": {
                    "$ref": "#/components/schemas/LinesAndBytes",
                    "description": "In the log on the machine, read as ever, and not kept for good (not shipped to the object store): at their time the account had used up the logs its plan includes and held nothing to pay for more (`meters.logs` of `GET /v1/billing` was `stopped`; the log says where that began and ended, `logs.keep_off` and `logs.keep_on` with the reason `over_plan`). They are not counted against what the plan includes, and cost nothing."
                  },
                  "node_buffer": {
                    "$ref": "#/components/schemas/LinesAndBytes",
                    "description": "Kept, and let go of by their machine before it had shipped them to the object store (`logs.gap`, `node_buffer`), by the bytes they were kept as."
                  },
                  "node_lost": {
                    "type": "object",
                    "required": [
                      "lines"
                    ],
                    "description": "Of a machine that went (`logs.gap`, `node_gone` and `log_gone`): the lines it had not shipped, at the most, by the time of the last line it had shipped. Of every kind: the machine did not say which.",
                    "properties": {
                      "lines": {
                        "type": "integer",
                        "minimum": 0
                      }
                    }
                  }
                }
              },
              "cut": {
                "$ref": "#/components/schemas/LinesAndBytes",
                "description": "Lines longer than a line may be, and the bytes cut off them (`cut_bytes`)."
              },
              "lost": {
                "type": "object",
                "description": "What was gone before the machine read it (`output.lost`).",
                "required": [
                  "times",
                  "bytes"
                ],
                "properties": {
                  "times": {
                    "type": "integer",
                    "minimum": 0
                  },
                  "bytes": {
                    "type": "integer",
                    "format": "int64",
                    "minimum": 0,
                    "description": "Where they are known."
                  }
                }
              }
            }
          },
          "requests": {
            "type": "object",
            "description": "The requests the machines handled: given to an instance and answered, or refused.",
            "required": [
              "handled",
              "kept",
              "not_kept"
            ],
            "properties": {
              "handled": {
                "type": "integer",
                "minimum": 0,
                "description": "`kept.requests`, `not_kept.sampled.lines` and `not_kept.node_buffer.requests`."
              },
              "kept": {
                "type": "object",
                "required": [
                  "lines",
                  "requests"
                ],
                "properties": {
                  "lines": {
                    "type": "integer",
                    "minimum": 0,
                    "description": "The request lines kept."
                  },
                  "requests": {
                    "type": "integer",
                    "minimum": 0,
                    "description": "The requests they stand for: a line of refusals stands for as many as its `times`."
                  }
                }
              },
              "not_kept": {
                "type": "object",
                "required": [
                  "sampled",
                  "node_buffer"
                ],
                "properties": {
                  "sampled": {
                    "type": "object",
                    "required": [
                      "lines"
                    ],
                    "properties": {
                      "lines": {
                        "type": "integer",
                        "minimum": 0,
                        "description": "The requests whose line was left out for the app's sample (`logs.requests.sample_rate`)."
                      }
                    }
                  },
                  "node_buffer": {
                    "type": "object",
                    "required": [
                      "lines",
                      "requests"
                    ],
                    "description": "Request lines that were kept, and let go of by their machine before it had shipped them (`logs.gap`), and the requests they stood for.",
                    "properties": {
                      "lines": {
                        "type": "integer",
                        "minimum": 0
                      },
                      "requests": {
                        "type": "integer",
                        "minimum": 0
                      }
                    }
                  }
                }
              }
            }
          },
          "platform": {
            "type": "object",
            "required": [
              "kept",
              "not_kept"
            ],
            "properties": {
              "kept": {
                "type": "object",
                "required": [
                  "lines"
                ],
                "properties": {
                  "lines": {
                    "type": "integer",
                    "minimum": 0
                  }
                }
              },
              "not_kept": {
                "type": "object",
                "required": [
                  "node_buffer"
                ],
                "properties": {
                  "node_buffer": {
                    "type": "object",
                    "required": [
                      "lines"
                    ],
                    "description": "The platform's lines that were kept, and let go of by their machine before it had shipped them.",
                    "properties": {
                      "lines": {
                        "type": "integer",
                        "minimum": 0
                      }
                    }
                  }
                }
              }
            }
          },
          "edge_refused": {
            "type": "object",
            "additionalProperties": {
              "type": "integer",
              "minimum": 0
            },
            "description": "By the error code, the requests the edges refused themselves (`app_not_deployed`, a stopped project's code, `too_many_connections`, `node_unavailable`, `app_at_capacity` where no machine had room): none of them reached a machine, and no line of the log has them. `{}`: none."
          },
          "exported": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AppLogExported"
            },
            "description": "What was exported of it, by destination (`/v1/destinations`): those the nodes counted lines of in the day (or the hour), sent or not. `[]`: none."
          }
        }
      },
      "LinesAndBytes": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "lines",
          "bytes"
        ],
        "properties": {
          "lines": {
            "type": "integer",
            "minimum": 0
          },
          "bytes": {
            "type": "integer",
            "format": "int64",
            "minimum": 0
          }
        }
      },
      "AppLogExported": {
        "type": "object",
        "additionalProperties": false,
        "description": "What was exported of the log to one destination in the day (or the hour), every node's added up.",
        "required": [
          "destination",
          "destination_id",
          "sent",
          "not_sent"
        ],
        "properties": {
          "destination": {
            "type": [
              "string",
              "null"
            ],
            "description": "Its name (the last it had, for one that was deleted)."
          },
          "destination_id": {
            "type": "string",
            "pattern": "^dst-[0-9a-hjkmnp-tv-z]{16}$"
          },
          "sent": {
            "$ref": "#/components/schemas/ExportSent",
            "description": "The lines and points sent, and the bytes of the requests they went in (booked as the account's traffic)."
          },
          "not_sent": {
            "$ref": "#/components/schemas/ExportNotSent"
          }
        }
      },
      "ExportSent": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "lines",
          "bytes"
        ],
        "properties": {
          "lines": {
            "type": "integer",
            "format": "int64",
            "minimum": 0,
            "description": "Lines of logs sent."
          },
          "bytes": {
            "type": "integer",
            "format": "int64",
            "minimum": 0,
            "description": "The bytes of the requests sent, of logs, of metrics and of traces, head and body as they went: booked as the account's traffic (`log_export`)."
          },
          "points": {
            "type": "integer",
            "format": "int64",
            "minimum": 0,
            "description": "Points of metrics sent."
          },
          "spans": {
            "type": "integer",
            "format": "int64",
            "minimum": 0,
            "description": "Spans sent."
          }
        }
      },
      "ExportNotSent": {
        "type": "object",
        "additionalProperties": false,
        "description": "The lines that were not exported, by why; of metrics, the points refused and the minutes not sent.",
        "required": [
          "rejected",
          "destination_down",
          "destination_slow",
          "egress_stopped",
          "exporter_down",
          "node_buffer"
        ],
        "properties": {
          "node_buffer": {
            "$ref": "#/components/schemas/ExportLines",
            "description": "The node let them go before the destination took them: the app had all the node keeps of one app, as it does when the destination is far behind for longer than the node can hold the lines (the app's log says so in a line of `export.dropped` with this reason, which the destination is sent)."
          },
          "rejected": {
            "$ref": "#/components/schemas/ExportLines",
            "description": "Refused by the destination: in a batch it took the rest of (a partial success), or in a batch it answered with a status that is not sent again (a 4xx but 429, a redirect)."
          },
          "destination_down": {
            "$ref": "#/components/schemas/ExportLines",
            "description": "It could not be reached, or did not take them, while they were new enough (ten minutes), or they were past the last 64 MiB of a log it had not taken."
          },
          "destination_slow": {
            "$ref": "#/components/schemas/ExportLines",
            "description": "It took them more slowly than they came: they were past the last 64 MiB of a log it had not taken."
          },
          "egress_stopped": {
            "$ref": "#/components/schemas/ExportLines",
            "description": "The account's traffic to the internet was stopped while they were new enough."
          },
          "exporter_down": {
            "$ref": "#/components/schemas/ExportLines",
            "description": "No exporter ran on their node while they were new enough."
          }
        }
      },
      "ExportLines": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "lines"
        ],
        "properties": {
          "lines": {
            "type": "integer",
            "format": "int64",
            "minimum": 0,
            "description": "Lines of logs."
          },
          "points": {
            "type": "integer",
            "format": "int64",
            "minimum": 0,
            "description": "Of `rejected`: points of metrics the destination refused."
          },
          "minutes": {
            "type": "integer",
            "format": "int64",
            "minimum": 0,
            "description": "Of metrics: minutes not sent (or waiting to be)."
          },
          "spans": {
            "type": "integer",
            "format": "int64",
            "minimum": 0,
            "description": "Of traces: spans refused or not sent (or waiting to be)."
          }
        }
      },
      "AppMetrics": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "app",
          "step",
          "from",
          "to",
          "data"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "app_metrics"
          },
          "app": {
            "type": "string",
            "pattern": "^app-[0-9a-hjkmnp-tv-z]{16}$"
          },
          "step": {
            "type": "string",
            "enum": [
              "minute",
              "hour"
            ]
          },
          "from": {
            "type": "string",
            "format": "date-time"
          },
          "to": {
            "type": "string",
            "format": "date-time"
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AppMetricsStep"
            }
          }
        }
      },
      "AppMetricsStep": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "requests",
          "duration_ms",
          "instances",
          "requests_in_hand",
          "wakes",
          "made",
          "cpu_seconds",
          "memory_bytes",
          "nodes"
        ],
        "description": "A minute or an hour of an app, every node's added up.",
        "properties": {
          "minute": {
            "type": "string",
            "format": "date-time",
            "description": "With `step=minute`: its beginning."
          },
          "hour": {
            "type": "string",
            "format": "date-time",
            "description": "With `step=hour`: its beginning."
          },
          "requests": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "total",
              "refused",
              "by_class"
            ],
            "properties": {
              "total": {
                "type": "integer",
                "minimum": 0,
                "description": "Every request a node handled: what its log counts as handled."
              },
              "refused": {
                "type": "integer",
                "minimum": 0,
                "description": "Those a node refused (the app full there, at its cap, ...): not counted for the bill."
              },
              "by_class": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "1xx",
                  "2xx",
                  "3xx",
                  "4xx",
                  "5xx",
                  "none"
                ],
                "properties": {
                  "1xx": {
                    "type": "integer",
                    "minimum": 0
                  },
                  "2xx": {
                    "type": "integer",
                    "minimum": 0
                  },
                  "3xx": {
                    "type": "integer",
                    "minimum": 0
                  },
                  "4xx": {
                    "type": "integer",
                    "minimum": 0
                  },
                  "5xx": {
                    "type": "integer",
                    "minimum": 0
                  },
                  "none": {
                    "type": "integer",
                    "minimum": 0
                  }
                },
                "description": "By the class of the status; `none`: the instance closed the connection without an answer."
              }
            }
          },
          "duration_ms": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "p50",
              "p99"
            ],
            "description": "How long the requests took, from the request's head to the last of its answer, as the buckets of all of them say; null: no request.",
            "properties": {
              "p50": {
                "type": [
                  "number",
                  "null"
                ]
              },
              "p99": {
                "type": [
                  "number",
                  "null"
                ]
              }
            }
          },
          "instances": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "awake",
              "stopped",
              "total"
            ],
            "properties": {
              "awake": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "last",
                  "max"
                ],
                "properties": {
                  "last": {
                    "type": "integer",
                    "minimum": 0
                  },
                  "max": {
                    "type": "integer",
                    "minimum": 0
                  }
                },
                "description": "`last`: the nodes' last reading of the step added up; `max`: the most of the step's minutes, the nodes' added (of hours kept by the hour, each node's most added: an upper bound)."
              },
              "stopped": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "last",
                  "max"
                ],
                "properties": {
                  "last": {
                    "type": "integer",
                    "minimum": 0
                  },
                  "max": {
                    "type": "integer",
                    "minimum": 0
                  }
                },
                "description": "`last`: the nodes' last reading of the step added up; `max`: the most of the step's minutes, the nodes' added (of hours kept by the hour, each node's most added: an upper bound)."
              },
              "total": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "last",
                  "max"
                ],
                "properties": {
                  "last": {
                    "type": "integer",
                    "minimum": 0
                  },
                  "max": {
                    "type": "integer",
                    "minimum": 0
                  }
                },
                "description": "`last`: the nodes' last reading of the step added up; `max`: the most of the step's minutes, the nodes' added (of hours kept by the hour, each node's most added: an upper bound)."
              }
            }
          },
          "requests_in_hand": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "last",
              "max"
            ],
            "properties": {
              "last": {
                "type": "integer",
                "minimum": 0
              },
              "max": {
                "type": "integer",
                "minimum": 0
              }
            },
            "description": "`last`: the nodes' last reading of the step added up; `max`: the most of the step's minutes, the nodes' added (of hours kept by the hour, each node's most added: an upper bound)."
          },
          "wakes": {
            "type": "integer",
            "minimum": 0
          },
          "made": {
            "type": "integer",
            "minimum": 0,
            "description": "Instances made."
          },
          "cpu_seconds": {
            "type": "number",
            "minimum": 0
          },
          "memory_bytes": {
            "type": "number",
            "minimum": 0,
            "description": "The memory its instances held, on average over the step."
          },
          "nodes": {
            "type": "integer",
            "minimum": 0,
            "description": "How many nodes said something of the app in the step."
          }
        }
      },
      "DestinationCreate": {
        "type": "object",
        "additionalProperties": false,
        "description": "A destination: its `name`, and its `endpoint`, or the address of each signal it takes (`logs_endpoint`, `metrics_endpoint`). The rest is as each field says when it is not given.",
        "required": [
          "name"
        ],
        "properties": {
          "name": {
            "type": "string",
            "description": "1 to 64 lowercase letters, digits, `-` and `_`, beginning with a letter or a digit, and not beginning with `dst-`; one destination of the project has it (`invalid_name`, `destination_name_taken`)."
          },
          "type": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "otlp",
              null
            ],
            "description": "`otlp`, the one there is: OpenTelemetry's protocol."
          },
          "endpoint": {
            "type": "string",
            "maxLength": 2048,
            "description": "OTLP's base address, `https://` only, with no user in it: the nodes post to it with `/v1/logs` added to its path (`https://otlp-gateway-prod-us-east-0.grafana.net/otlp` is posted to at `…/otlp/v1/logs`). Its host has to resolve to an address on the internet."
          },
          "logs_endpoint": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 2048,
            "description": "The logs' own address, `https://`, used as it is in place of `endpoint` and `/v1/logs`. Null: `endpoint` and `/v1/logs` again."
          },
          "headers": {
            "type": [
              "object",
              "null"
            ],
            "maxProperties": 20,
            "additionalProperties": {
              "type": "string",
              "maxLength": 8192
            },
            "description": "The headers each request carries, by name: the credentials a vendor asks for (`Authorization`, `x-honeycomb-team`, `dd-api-key`), each value one line of text. None of those the exporter writes itself or that would change what the request is: `Host`, `Content-Length`, `Content-Type`, `Content-Encoding`, `Transfer-Encoding`, `Connection`, `Keep-Alive`, `TE`, `Trailer`, `Upgrade`, `Expect`, `Proxy-Authorization`."
          },
          "protocol": {
            "type": "string",
            "enum": [
              "http/protobuf",
              "http/json"
            ],
            "default": "http/protobuf",
            "description": "OTLP over HTTP, its records as protobuf or as JSON. gRPC is not taken."
          },
          "compression": {
            "type": "string",
            "enum": [
              "gzip",
              "none"
            ],
            "default": "gzip"
          },
          "signals": {
            "type": [
              "array",
              "null"
            ],
            "minItems": 1,
            "items": {
              "type": "string",
              "enum": [
                "logs",
                "metrics",
                "traces"
              ]
            },
            "description": "What is exported to it: some of the apps' logs, their metrics (every minute, from every node: `sbx.node.id` tells the nodes apart) and the spans of their requests (`traces`, as each app's `traces` samples them). Null, or not given: `logs`."
          },
          "metrics_endpoint": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 2048,
            "description": "The metrics' own address, `https://`, used as it is in place of `endpoint` and `/v1/metrics`. Null: `endpoint` and `/v1/metrics` again."
          },
          "traces_endpoint": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 2048,
            "description": "The traces' own address, `https://`, used as it is in place of `endpoint` and `/v1/traces`. Null: `endpoint` and `/v1/traces` again."
          },
          "temporality": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "cumulative",
              "delta",
              null
            ],
            "description": "How the metrics' sums and histograms add up: `cumulative` (null, or not given), all since the node began counting; `delta`, each minute's own, which some receivers want (Datadog's)."
          },
          "kinds": {
            "type": [
              "array",
              "null"
            ],
            "minItems": 1,
            "items": {
              "type": "string",
              "enum": [
                "output",
                "requests",
                "platform"
              ]
            },
            "description": "The kinds of lines it takes: what the instances printed, the lines of their requests, the platform's. Null, or not given: all three. Whatever it takes, a line of the platform's that says lines were not exported to it (`export.dropped`) goes to it, and to no other."
          },
          "apps": {
            "oneOf": [
              {
                "type": "string",
                "const": "all"
              },
              {
                "type": "array",
                "maxItems": 1000,
                "items": {
                  "type": "string"
                }
              },
              {
                "type": "null"
              }
            ],
            "description": "`all` (null, or not given): every app of the project, those made later too. Or some of its apps, by name or id: of each, the lines from when it was taken on."
          },
          "resource_attributes": {
            "type": [
              "object",
              "null"
            ],
            "maxProperties": 16,
            "additionalProperties": {
              "type": "string",
              "maxLength": 1024
            },
            "description": "What its owner adds to the resource of every record: `deployment.environment.name`, say. Names of 1 to 256 characters; none of those the platform sets (`service.name`, `service.namespace`, `service.version`, `service.instance.id`, `cloud.provider`, `cloud.platform`, `cloud.region`, `faas.name`, `faas.version`, `faas.instance`, `faas.max_memory`, `container.image.name`, `container.image.repo_digests`, `sbx.app.id`, `sbx.version.number`, `sbx.node.region`, `sbx.node.id`)."
          }
        }
      },
      "Destination": {
        "type": "object",
        "additionalProperties": false,
        "description": "A destination of the logs, the metrics and the traces of the project's apps: what it is, its headers by name, and how it stands.",
        "required": [
          "id",
          "object",
          "name",
          "type",
          "endpoint",
          "headers",
          "protocol",
          "compression",
          "signals",
          "kinds",
          "apps",
          "resource_attributes",
          "created_at",
          "updated_at",
          "status"
        ],
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^dst-[0-9a-hjkmnp-tv-z]{16}$",
            "description": "What the usage of the account's traffic names it by (`GET /v1/usage`, `kind=log_export`)."
          },
          "object": {
            "type": "string",
            "const": "destination"
          },
          "name": {
            "type": "string"
          },
          "type": {
            "type": "string",
            "const": "otlp"
          },
          "endpoint": {
            "type": [
              "string",
              "null"
            ],
            "description": "OTLP's base address; null where `logs_endpoint` alone was given."
          },
          "logs_endpoint": {
            "type": "string",
            "description": "The logs' own address, where one was given: what the nodes post to."
          },
          "headers": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "The names of its headers: their values are in no answer."
          },
          "protocol": {
            "type": "string",
            "enum": [
              "http/protobuf",
              "http/json"
            ]
          },
          "compression": {
            "type": "string",
            "enum": [
              "gzip",
              "none"
            ]
          },
          "signals": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "logs",
                "metrics",
                "traces"
              ]
            },
            "description": "What is exported to it."
          },
          "metrics_endpoint": {
            "type": "string",
            "description": "The metrics' own address, where one was given: what the nodes post their minutes to."
          },
          "traces_endpoint": {
            "type": "string",
            "description": "The traces' own address, where one was given: what the nodes post their spans to."
          },
          "temporality": {
            "type": "string",
            "enum": [
              "cumulative",
              "delta"
            ],
            "description": "How its metrics' sums and histograms add up."
          },
          "kinds": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "output",
                "requests",
                "platform"
              ]
            }
          },
          "apps": {
            "oneOf": [
              {
                "type": "string",
                "const": "all"
              },
              {
                "type": "array",
                "items": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "id",
                    "name",
                    "since"
                  ],
                  "properties": {
                    "id": {
                      "type": "string",
                      "pattern": "^app-[0-9a-hjkmnp-tv-z]{16}$"
                    },
                    "name": {
                      "type": "string"
                    },
                    "since": {
                      "type": "string",
                      "format": "date-time",
                      "description": "When it was taken on: its lines from then on."
                    }
                  }
                }
              }
            ],
            "description": "`all`: every app of the project; or the apps it takes."
          },
          "resource_attributes": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "status": {
            "$ref": "#/components/schemas/DestinationStatus"
          }
        }
      },
      "DestinationStatus": {
        "type": "object",
        "additionalProperties": false,
        "description": "How a destination stands: the word on it of every node whose exporter said in the last minute how it stands there, put together.",
        "required": [
          "state",
          "connected",
          "connected_at",
          "tried",
          "last_sent_at",
          "last_error",
          "waiting",
          "nodes",
          "today"
        ],
        "properties": {
          "state": {
            "type": "string",
            "x-extensible-enum": [
              "not_connected",
              "stopped",
              "failing",
              "retrying",
              "ok",
              "idle"
            ],
            "description": "`not_connected`: no try was answered 2xx, and the nodes send it nothing. `stopped`: the account's traffic to the internet is stopped, and so is what is sent to it. `failing`: a node's exporter says so: the endpoint answered a status that is not sent again, its address is inside, or it has not taken a batch for a minute that was sent again. `retrying`: one sends a batch again, and none fails. `ok`: the nodes that said something send to it. `idle`: none said anything (no node keeps a log of the apps it takes)."
          },
          "connected": {
            "type": "boolean",
            "description": "A try was answered 2xx: the nodes send to it."
          },
          "connected_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Since when: no line before is its."
          },
          "tried": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/DestinationTried"
              },
              {
                "type": "null"
              }
            ],
            "description": "The last try, when it was made, changed where it is reached, or tested."
          },
          "last_sent_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When a node's exporter last had a batch taken by it."
          },
          "last_error": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": false,
            "required": [
              "at",
              "status",
              "code",
              "message"
            ],
            "description": "What went wrong last, on whichever node: null where nothing has.",
            "properties": {
              "at": {
                "type": "string",
                "format": "date-time"
              },
              "status": {
                "type": [
                  "integer",
                  "null"
                ],
                "description": "The status of the endpoint's answer; null: there was none."
              },
              "code": {
                "type": [
                  "string",
                  "null"
                ],
                "x-extensible-enum": [
                  "status",
                  "redirect",
                  "address",
                  "unreachable",
                  "no_answer",
                  "partial_success"
                ],
                "description": "`status`: it answered with an error; `redirect`: with a redirect, which is not followed; `address`: its host leads to an address inside; `unreachable`: no connection, or TLS failed; `no_answer`: the request went and nothing came back in time; `partial_success`: it took a batch and refused some of its records."
              },
              "message": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "What it said (512 bytes at the most), or why there was no answer, with every header's value cut out."
              }
            }
          },
          "waiting": {
            "$ref": "#/components/schemas/ExportLines",
            "description": "How many lines the nodes hold for it that it has not taken, minutes of metrics, and spans."
          },
          "signals": {
            "type": "object",
            "additionalProperties": false,
            "description": "Each signal it takes, as the nodes say it stands put together (`failing` where one node's is, then `retrying`, `ok`); of traces, with the spans not sent to it because they went before a node's exporter read them (a node holds a project's last 2,048) or waited longer than ten minutes.",
            "properties": {
              "logs": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "state"
                ],
                "properties": {
                  "state": {
                    "type": "string"
                  }
                }
              },
              "metrics": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "state"
                ],
                "properties": {
                  "state": {
                    "type": "string"
                  }
                }
              },
              "traces": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "state",
                  "dropped"
                ],
                "properties": {
                  "state": {
                    "type": "string"
                  },
                  "dropped": {
                    "type": "integer",
                    "minimum": 0
                  }
                }
              }
            }
          },
          "nodes": {
            "type": "integer",
            "minimum": 0,
            "description": "How many nodes said how it stands there in the last minute."
          },
          "today": {
            "$ref": "#/components/schemas/ExportCounts",
            "description": "What was exported to it today (UTC), every node's added up."
          }
        }
      },
      "DestinationTried": {
        "type": "object",
        "additionalProperties": false,
        "description": "A try: the one record the control plane sent the destination of its logs, the one point of a gauge of its metrics and the one span of its traces (each `glac.destination.tried`), and what came of them: the first that was not taken, or the last, with each by itself (`signals`).",
        "required": [
          "at",
          "status",
          "ok",
          "message"
        ],
        "properties": {
          "signals": {
            "type": "object",
            "additionalProperties": false,
            "properties": {
              "logs": {
                "$ref": "#/components/schemas/DestinationTriedSignal"
              },
              "metrics": {
                "$ref": "#/components/schemas/DestinationTriedSignal"
              },
              "traces": {
                "$ref": "#/components/schemas/DestinationTriedSignal"
              }
            }
          },
          "at": {
            "type": "string",
            "format": "date-time"
          },
          "status": {
            "type": [
              "integer",
              "null"
            ],
            "description": "The status of the endpoint's answer. Null: there was none (it does not resolve, its address is not on the internet, it could not be reached, TLS failed, no answer in ten seconds)."
          },
          "ok": {
            "type": "boolean",
            "description": "It was answered 2xx, and no record was refused."
          },
          "message": {
            "type": "string",
            "description": "What the endpoint said (its partial success's or its error's words, 512 bytes at the most, with every header's value cut out), or why there was no answer."
          }
        }
      },
      "DestinationTriedSignal": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "at",
          "status",
          "ok",
          "message"
        ],
        "properties": {
          "at": {
            "type": "string",
            "format": "date-time"
          },
          "status": {
            "type": [
              "integer",
              "null"
            ]
          },
          "ok": {
            "type": "boolean"
          },
          "message": {
            "type": "string"
          }
        }
      },
      "ExportCounts": {
        "type": "object",
        "additionalProperties": false,
        "description": "What the nodes' exporters counted: the lines sent, and the bytes of the requests they were sent in, head and body as it went (compressed, where it was); the lines not sent.",
        "required": [
          "sent",
          "not_sent"
        ],
        "properties": {
          "sent": {
            "$ref": "#/components/schemas/ExportSent"
          },
          "not_sent": {
            "$ref": "#/components/schemas/ExportNotSent"
          }
        }
      },
      "DestinationList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "data",
          "next_cursor"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Destination"
            }
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Where the next page begins; null on the last."
          }
        }
      },
      "DestinationUpdate": {
        "type": "object",
        "additionalProperties": false,
        "description": "What changes: a field that is not given is as it was. Each field as when the destination is made.",
        "properties": {
          "name": {
            "type": "string",
            "description": "A new name: 1 to 64 lowercase letters, digits, `-` and `_`, beginning with a letter or a digit, and not beginning with `dst-`; one destination of the project has it (`invalid_name`, `destination_name_taken`)."
          },
          "type": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "otlp",
              null
            ],
            "description": "`otlp`, the one there is. Null: the same."
          },
          "endpoint": {
            "type": "string",
            "maxLength": 2048,
            "description": "OTLP's base address, `https://` only, with no user in it: the nodes post to it with `/v1/logs` added to its path (`https://otlp-gateway-prod-us-east-0.grafana.net/otlp` is posted to at `…/otlp/v1/logs`). Its host has to resolve to an address on the internet."
          },
          "logs_endpoint": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 2048,
            "description": "The logs' own address, `https://`, used as it is in place of `endpoint` and `/v1/logs`. Null: `endpoint` and `/v1/logs` again."
          },
          "headers": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": {
              "type": [
                "string",
                "null"
              ],
              "maxLength": 8192
            },
            "description": "Each header named is put, or taken away by null; those it does not name are kept. Null: no header at all. 20 at the most in the end."
          },
          "protocol": {
            "type": "string",
            "enum": [
              "http/protobuf",
              "http/json"
            ],
            "default": "http/protobuf",
            "description": "OTLP over HTTP, its records as protobuf or as JSON. gRPC is not taken."
          },
          "compression": {
            "type": "string",
            "enum": [
              "gzip",
              "none"
            ],
            "default": "gzip"
          },
          "signals": {
            "type": [
              "array",
              "null"
            ],
            "minItems": 1,
            "items": {
              "type": "string",
              "enum": [
                "logs",
                "metrics",
                "traces"
              ]
            },
            "description": "What is exported to it: some of `logs`, `metrics` and `traces`. Null: `logs`."
          },
          "metrics_endpoint": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 2048,
            "description": "The metrics' own address, `https://`, used as it is in place of `endpoint` and `/v1/metrics`. Null: `endpoint` and `/v1/metrics` again."
          },
          "traces_endpoint": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 2048,
            "description": "The traces' own address, `https://`, used as it is in place of `endpoint` and `/v1/traces`. Null: `endpoint` and `/v1/traces` again."
          },
          "temporality": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "cumulative",
              "delta",
              null
            ],
            "description": "How the metrics' sums and histograms add up: `cumulative` (null, or not given), all since the node began counting; `delta`, each minute's own, which some receivers want (Datadog's)."
          },
          "kinds": {
            "type": [
              "array",
              "null"
            ],
            "minItems": 1,
            "items": {
              "type": "string",
              "enum": [
                "output",
                "requests",
                "platform"
              ]
            },
            "description": "The kinds of lines it takes, in place of those it took. Null: all three."
          },
          "apps": {
            "oneOf": [
              {
                "type": "string",
                "const": "all"
              },
              {
                "type": "array",
                "maxItems": 1000,
                "items": {
                  "type": "string"
                }
              },
              {
                "type": "null"
              }
            ],
            "description": "`all`, or null: every app of the project, those made later too. Or some of its apps, by name or id: of an app it took before, the lines from when it was taken on; of one it takes now, from now on."
          },
          "resource_attributes": {
            "type": [
              "object",
              "null"
            ],
            "maxProperties": 16,
            "additionalProperties": {
              "type": "string",
              "maxLength": 1024
            },
            "description": "What its owner adds to the resource of every record: `deployment.environment.name`, say. Names of 1 to 256 characters; none of those the platform sets (`service.name`, `service.namespace`, `service.version`, `service.instance.id`, `cloud.provider`, `cloud.platform`, `cloud.region`, `faas.name`, `faas.version`, `faas.instance`, `faas.max_memory`, `container.image.name`, `container.image.repo_digests`, `sbx.app.id`, `sbx.version.number`, `sbx.node.region`, `sbx.node.id`). In place of those it had; null: none."
          }
        }
      },
      "DestinationTest": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "destination",
          "test"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "destination_test"
          },
          "destination": {
            "$ref": "#/components/schemas/Destination"
          },
          "test": {
            "$ref": "#/components/schemas/DestinationTried"
          }
        }
      },
      "InstanceCreate": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "key"
        ],
        "properties": {
          "key": {
            "type": "string",
            "minLength": 1,
            "maxLength": 128,
            "description": "The key: 1 to 128 characters, none of them a control character (U+0000 to U+001F, U+007F to U+009F)."
          },
          "region": {
            "type": "string",
            "enum": [
              "na",
              "eu",
              "ap"
            ],
            "description": "Where a new instance is made: one of the regions the app runs in. Left out: the first the app names. Of an instance the key names already, nothing."
          }
        }
      },
      "Instance": {
        "type": "object",
        "additionalProperties": true,
        "required": [
          "id",
          "object",
          "status",
          "app_id",
          "version_id",
          "key",
          "region"
        ],
        "description": "A keyed instance: a sandbox of the app's project, made from the template of the version that had the app's traffic when its key first named it. Every field of a sandbox's object is here (`Sandbox` in `sandboxd/openapi/openapi.json`), `object` aside; the ones that make it a key's are named below.",
        "properties": {
          "id": {
            "type": "string",
            "description": "The sandbox's id (`sbx-...`): what `/v1/sandboxes/{id}/...` answers it under."
          },
          "object": {
            "type": "string",
            "enum": [
              "instance"
            ]
          },
          "status": {
            "type": "string",
            "description": "A sandbox's status; `unavailable` while its node cannot be reached."
          },
          "metadata": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "The app's labels, and `app`, `version`, `region` and `key`: what a table that is built again from the nodes reads."
          },
          "app_id": {
            "type": "string"
          },
          "version_id": {
            "type": "string",
            "description": "The version it was made from; a version deployed later leaves it on this one."
          },
          "key": {
            "type": "string"
          },
          "region": {
            "type": "string",
            "x-extensible-enum": [
              "na",
              "eu",
              "ap"
            ],
            "description": "The region it was made in."
          },
          "start_command": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/InstanceStartCommand"
              },
              {
                "type": "null"
              }
            ],
            "description": "How its start command runs, how often it was started again in its machine after it ended (a key's instance and a stateful service's are, with their disk and volumes as they are; each start is an `instance.restarted`), how its last run ended, and when the next start is due while one waits: `status` (`running`, `restarting`, `exited`), `restarts`, `last_exit` (`status`, `signal`, `at`), `restarting_at`, as `SandboxStartCommand` in `sandboxd/openapi/openapi.json` says. Null where its guest has said nothing of it yet."
          },
          "delete_after_idle_ms": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Its own time asleep after which it is deleted, as a sandbox's (`PATCH /v1/sandboxes/{id}`): where it has one, it goes by that and not by its app's `keys.delete_after_idle_ms`. `null`: its app's."
          },
          "idle_since": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When its current sleep began, as its node says (in a list, as the control plane last saw its object), or, for one that was frozen, when it was let go of: what both times asleep are counted from. `null` while it is awake."
          }
        }
      },
      "InstanceStartCommand": {
        "type": "object",
        "additionalProperties": false,
        "description": "A key's instance's start command, or a database branch's server's, as its sandbox's guest says it: the node's `SandboxStartCommand` (sandboxd/openapi/openapi.json), which says when it is started again. Where the node has its guest start it again (an instance that is its owner's state), that is once its process has ended and nothing it left behind holds its output, after a wait that doubles with every run that ends within 10 s of its start: 0.1 s after the first, then 0.2, 0.4 and so on, a minute at the most; a run of 10 s or more starts the waits from 0.1 s again (Docker's restart policy `always`, the same numbers). An end the node asks for (its stop: a delete, a restart from its disk, a change of version) is no reason to start it again.",
        "required": [
          "status",
          "restarts",
          "last_exit",
          "restarting_at"
        ],
        "properties": {
          "status": {
            "type": "string",
            "x-extensible-enum": [
              "running",
              "restarting",
              "exited"
            ],
            "description": "`running`: a run of it is under way (or what it left behind still holds its output); `restarting`: it ended, and is started again at `restarting_at`; `exited`: it ended, and nothing starts it again."
          },
          "restarts": {
            "type": "integer",
            "format": "int64",
            "minimum": 0,
            "description": "How often it was started again in its machine since the sandbox was made, across the boots of its machines."
          },
          "last_exit": {
            "oneOf": [
              {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "status",
                  "signal",
                  "at"
                ],
                "properties": {
                  "status": {
                    "type": "integer",
                    "description": "Its exit status as a shell says it: the code it ended with, or 128 + the signal's number where a signal ended it."
                  },
                  "signal": {
                    "type": [
                      "integer",
                      "null"
                    ],
                    "description": "The signal that ended it, where one did (`status` above 128); null where it exited."
                  },
                  "at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When it ended, by its guest's clock."
                  }
                }
              },
              {
                "type": "null"
              }
            ],
            "description": "How its last run ended; null where none has."
          },
          "restarting_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "While `restarting`: when its next run begins, by its guest's clock (a machine asleep then starts it as it wakes). Null otherwise."
          }
        }
      },
      "InstanceList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "data",
          "next_cursor"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Instance"
            }
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "Upgrade": {
        "type": "object",
        "additionalProperties": false,
        "description": "An upgrade of a key's instance to another version of its app, in place (`POST /v1/apps/{app}/instances/{key}/upgrade`).",
        "required": [
          "id",
          "object",
          "app_id",
          "key",
          "instance_id",
          "kind",
          "from_version",
          "to_version",
          "status",
          "when",
          "keep_days",
          "rollback",
          "reverts",
          "batch",
          "timings",
          "error",
          "put_back",
          "created_at",
          "started_at",
          "ended_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^upg-[0-9a-hjkmnp-tv-z]{16}$"
          },
          "object": {
            "type": "string",
            "enum": [
              "upgrade"
            ]
          },
          "app_id": {
            "type": "string",
            "pattern": "^app-[0-9a-hjkmnp-tv-z]{16}$"
          },
          "key": {
            "type": "string"
          },
          "instance_id": {
            "type": "string",
            "pattern": "^sbx-[0-9a-hjkmnp-tv-z]{16}$",
            "description": "The instance, which keeps its id."
          },
          "kind": {
            "type": "string",
            "x-extensible-enum": [
              "upgrade",
              "revert"
            ],
            "description": "`revert`: back to what an upgrade kept (`reverts`)."
          },
          "from_version": {
            "type": "string",
            "pattern": "^ver-[0-9a-hjkmnp-tv-z]{16}$"
          },
          "to_version": {
            "type": "string",
            "pattern": "^ver-[0-9a-hjkmnp-tv-z]{16}$"
          },
          "status": {
            "type": "string",
            "x-extensible-enum": [
              "waiting",
              "preparing",
              "switching",
              "done",
              "failed",
              "cancelled"
            ],
            "description": "`waiting`: for the instance to sleep (`when: asleep`), or a batch's member for its turn, the instance serving as ever; `preparing`: the instance's node is given the new version's template, the instance serving meanwhile; `switching`: its node moves it over, its requests held at the edges and its calls answered 503 `upgrading` (but a read of it (`GET /v1/sandboxes/{id}`) answers 200 with it as it was last seen, `status` `upgrading`); `done`; `failed` (`error`, `put_back`); `cancelled` (while it waited: `DELETE`, or its batch stopped, `error` saying so)."
          },
          "when": {
            "type": "string",
            "enum": [
              "now",
              "asleep"
            ],
            "description": "`asleep`: it began once the instance slept (by its node's last inventory, its node making sure), and so held none of the requests the instance served while it waited."
          },
          "keep_days": {
            "type": "integer",
            "minimum": 0,
            "maximum": 365,
            "description": "How many days what a revert needs is kept (0: nothing)."
          },
          "rollback": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/UpgradeRollback"
              },
              {
                "type": "null"
              }
            ],
            "description": "What a revert of it needs, kept once it is done (null where it kept nothing)."
          },
          "reverts": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^upg-[0-9a-hjkmnp-tv-z]{16}$",
            "description": "A revert's: the upgrade it goes back from."
          },
          "batch": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^upb-[0-9a-hjkmnp-tv-z]{16}$",
            "description": "The batch it is a member of (`POST /v1/apps/{app}/upgrades`)."
          },
          "timings": {
            "type": "object",
            "additionalProperties": false,
            "description": "Milliseconds, each where its step was reached: `prepare_ms` (and `template`: how the node came to have it, `had`, `fetched` or `built`), `held_ms` (how long the edges held its requests), `node` (the node's own), `total_ms`.",
            "properties": {
              "prepare_ms": {
                "type": "integer",
                "minimum": 0,
                "description": "Until the instance's node had the new version's template."
              },
              "template": {
                "type": "string",
                "x-extensible-enum": [
                  "had",
                  "fetched",
                  "built"
                ],
                "description": "How the node came to have it."
              },
              "held_ms": {
                "type": "integer",
                "minimum": 0,
                "description": "How long the edges held the instance's requests."
              },
              "node": {
                "type": [
                  "object",
                  "null"
                ],
                "additionalProperties": false,
                "description": "The node's own steps, in milliseconds to the hundredth, as `SandboxUpgrade.timings` in `sandboxd/openapi/openapi.json` says them; null where the node said none.",
                "properties": {
                  "snapshot_ms": {
                    "type": "number",
                    "minimum": 0
                  },
                  "stop_ms": {
                    "type": "number",
                    "minimum": 0
                  },
                  "detach_ms": {
                    "type": "number",
                    "minimum": 0
                  },
                  "versions_ms": {
                    "type": "number",
                    "minimum": 0
                  },
                  "restore_ms": {
                    "type": "number",
                    "minimum": 0
                  },
                  "switch_ms": {
                    "type": "number",
                    "minimum": 0
                  },
                  "attach_ms": {
                    "type": "number",
                    "minimum": 0
                  },
                  "start_ms": {
                    "type": "number",
                    "minimum": 0
                  },
                  "total_ms": {
                    "type": "number",
                    "minimum": 0
                  }
                }
              },
              "total_ms": {
                "type": "integer",
                "minimum": 0,
                "description": "The whole upgrade, once it is over."
              }
            }
          },
          "error": {
            "type": [
              "object",
              "null"
            ],
            "required": [
              "code",
              "message"
            ],
            "properties": {
              "code": {
                "type": "string",
                "x-extensible-enum": [
                  "upgrade_failed",
                  "upgrade_not_here",
                  "rollback_gone",
                  "instance_gone",
                  "batch_stopped",
                  "superseded"
                ]
              },
              "message": {
                "type": "string"
              }
            },
            "description": "Where it failed: `upgrade_failed`, the new version did not come up (what it said is in `message`); `upgrade_not_here`, the instance's node could not have the new version's template, or refused the upgrade before touching anything (the node's words are in `message`), or a Drive the new version gives the key could not be made (the make's code and words are in `message`: `drive_limit_reached` where the plan does not let it be); `rollback_gone`, what a revert goes back to is gone. Where it was cancelled other than by `DELETE`: `instance_gone` (the key names another instance, or none), `batch_stopped` (its batch stopped, or was superseded), `superseded` (a stateful service's deploy came first)."
          },
          "put_back": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "Where it failed with `upgrade_failed`: whether the instance was put back as it was, its Drives with what they held before."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "started_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When its requests began to be held."
          },
          "ended_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "UpgradeRollback": {
        "type": "object",
        "additionalProperties": false,
        "description": "What an upgrade kept for a revert of it: a snapshot of the instance as it was before (a snapshot of the project's: listed, billed as one, deleted by its owner if they like), and a snapshot of each Drive it had as it was before (billed as a Drive's; not of one the new version mounts besides, which a revert leaves off as it is). Let go of (both deleted) once its `keep_days` are over by the books' clock (`gone_at`); a revert after that, or once one of them was deleted, is refused 409 `rollback_gone`.",
        "required": [
          "snapshot_id",
          "with_processes",
          "drives",
          "expires_at",
          "gone_at"
        ],
        "properties": {
          "snapshot_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "The instance's snapshot (`snap-…`)."
          },
          "with_processes": {
            "type": "boolean",
            "description": "A revert has the instance's processes back as they were (an instance without Drives); else it boots from the snapshot's disk and starts its version's start command once its Drives are mounted."
          },
          "drives": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "drive_id",
                "snapshot_id"
              ],
              "properties": {
                "drive_id": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "snapshot_id": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "description": "The Drive's snapshot (`dsnap-…`, named `before-upgrade-<id>` or `before-revert-<id>`); null where none could be kept (a revert is refused then)."
                }
              }
            }
          },
          "expires_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Until when it is kept, by the books' clock."
          },
          "gone_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When it was let go of."
          }
        }
      },
      "UpgradeList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "data",
          "next_cursor"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Upgrade"
            }
          },
          "next_cursor": {
            "type": "null"
          }
        }
      },
      "UpgradeBatch": {
        "type": "object",
        "additionalProperties": false,
        "description": "A batch of upgrades of an app's keys' instances (`POST /v1/apps/{app}/upgrades`).",
        "required": [
          "id",
          "object",
          "app_id",
          "to_version",
          "status",
          "stop_reason",
          "when",
          "keep_days",
          "concurrency",
          "max_failures",
          "origin",
          "counts",
          "upgrades",
          "skipped",
          "created_at",
          "ended_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^upb-[0-9a-hjkmnp-tv-z]{16}$"
          },
          "object": {
            "type": "string",
            "enum": [
              "upgrade_batch"
            ]
          },
          "app_id": {
            "type": "string",
            "pattern": "^app-[0-9a-hjkmnp-tv-z]{16}$"
          },
          "to_version": {
            "type": "string",
            "pattern": "^ver-[0-9a-hjkmnp-tv-z]{16}$"
          },
          "status": {
            "type": "string",
            "x-extensible-enum": [
              "running",
              "done",
              "failed",
              "stopped"
            ],
            "description": "`running`; `done` (every member is over); `failed` (`max_failures` of them failed: the ones that waited were cancelled, the ones done stay on the new version); `stopped` (`POST …/stop`, or a later deploy's batch superseded it: `stop_reason`)."
          },
          "stop_reason": {
            "type": [
              "string",
              "null"
            ],
            "x-extensible-enum": [
              "max_failures",
              "asked",
              "superseded"
            ]
          },
          "when": {
            "type": "string",
            "enum": [
              "now",
              "asleep"
            ]
          },
          "keep_days": {
            "type": "integer",
            "minimum": 0,
            "maximum": 365
          },
          "concurrency": {
            "type": "integer",
            "minimum": 1,
            "maximum": 64,
            "description": "Its members under way at once, at the most (and two on one node)."
          },
          "max_failures": {
            "type": "integer",
            "minimum": 1,
            "description": "How many of its members failing stop it."
          },
          "origin": {
            "type": "string",
            "x-extensible-enum": [
              "api",
              "deploy"
            ],
            "description": "`deploy`: made by a deploy of an app that says `keys.upgrade_on_deploy`."
          },
          "counts": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "total",
              "waiting",
              "preparing",
              "switching",
              "done",
              "failed",
              "cancelled"
            ],
            "properties": {
              "total": {
                "type": "integer"
              },
              "waiting": {
                "type": "integer",
                "description": "Those that wait: for their instance to sleep, or for their turn. An instance that never sleeps is counted here for as long as it stays awake."
              },
              "preparing": {
                "type": "integer"
              },
              "switching": {
                "type": "integer"
              },
              "done": {
                "type": "integer"
              },
              "failed": {
                "type": "integer"
              },
              "cancelled": {
                "type": "integer"
              }
            }
          },
          "upgrades": {
            "type": "array",
            "description": "Its members, the oldest first, the first 1,000 at the most (`counts` counts them all).",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "key",
                "upgrade_id",
                "instance_id",
                "status",
                "from_version",
                "error"
              ],
              "properties": {
                "key": {
                  "type": "string"
                },
                "upgrade_id": {
                  "type": "string",
                  "pattern": "^upg-[0-9a-hjkmnp-tv-z]{16}$"
                },
                "instance_id": {
                  "type": "string"
                },
                "status": {
                  "type": "string"
                },
                "from_version": {
                  "type": "string"
                },
                "error": {
                  "type": [
                    "object",
                    "null"
                  ],
                  "additionalProperties": false,
                  "required": [
                    "code",
                    "message"
                  ],
                  "description": "Where it failed or was cancelled (`batch_stopped`, `instance_gone`, a failure's own code); null otherwise.",
                  "properties": {
                    "code": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "skipped": {
            "type": "array",
            "description": "The keys left out because each had an upgrade of its own that was not over.",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "key",
                "upgrade_id"
              ],
              "properties": {
                "key": {
                  "type": "string"
                },
                "upgrade_id": {
                  "type": "string"
                }
              }
            }
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "ended_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "UpgradeBatchList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "data",
          "next_cursor"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/UpgradeBatch"
            }
          },
          "next_cursor": {
            "type": "null"
          }
        }
      },
      "InstancePortList": {
        "type": "object",
        "required": [
          "data",
          "next_cursor"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/InstancePort"
            }
          },
          "next_cursor": {
            "type": "null"
          }
        }
      },
      "InstancePort": {
        "type": "object",
        "required": [
          "object",
          "sandbox",
          "protocol",
          "port",
          "from",
          "address"
        ],
        "description": "A port open at the instance's own IPv6 address (`InboundPortShown` in `sandboxd/openapi/openapi.json`).",
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "inbound_port"
            ]
          },
          "sandbox": {
            "type": "string"
          },
          "protocol": {
            "type": "string",
            "enum": [
              "tcp",
              "udp"
            ]
          },
          "port": {
            "type": "integer",
            "minimum": 1,
            "maximum": 65535
          },
          "from": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "address": {
            "type": [
              "string",
              "null"
            ],
            "description": "`[<ipv6>]:<port>`."
          }
        }
      },
      "InstancePortOpen": {
        "type": "object",
        "required": [
          "from"
        ],
        "properties": {
          "from": {
            "type": "array",
            "minItems": 1,
            "maxItems": 32,
            "items": {
              "type": "string"
            },
            "description": "The IPv6 prefixes the port is open to: `2001:db8::/32`, a single address, `::/0` for everybody."
          }
        }
      },
      "InstancePortClosed": {
        "type": "object",
        "required": [
          "object",
          "sandbox",
          "protocol",
          "port",
          "closed",
          "was_open"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "inbound_port"
            ]
          },
          "sandbox": {
            "type": "string"
          },
          "protocol": {
            "type": "string",
            "enum": [
              "tcp",
              "udp"
            ]
          },
          "port": {
            "type": "integer",
            "minimum": 1,
            "maximum": 65535
          },
          "closed": {
            "type": "boolean",
            "enum": [
              true
            ]
          },
          "was_open": {
            "type": "boolean"
          }
        }
      },
      "KeySecretMade": {
        "type": "object",
        "additionalProperties": false,
        "description": "A secret that was just made, with its value: shown this once.",
        "required": [
          "id",
          "object",
          "app_id",
          "prefix",
          "created_at",
          "secret"
        ],
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^ksec-[0-9a-hjkmnp-tv-z]{16}$"
          },
          "object": {
            "type": "string",
            "const": "key_secret"
          },
          "app_id": {
            "type": "string",
            "description": "The app whose keys it signs."
          },
          "prefix": {
            "type": "string",
            "description": "The secret's first 11 characters (`ks_` and 8 more), by which a list tells the two apart."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "secret": {
            "type": "string",
            "pattern": "^ks_[0-9a-hjkmnp-tv-z]{52}$",
            "description": "`ks_` and 52 characters (256 random bits). A signature is keyed with this text as it is."
          }
        }
      },
      "KeySecretList": {
        "type": "object",
        "additionalProperties": false,
        "description": "An app's key secrets, the newest first: two at the most.",
        "required": [
          "data",
          "next_cursor"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/KeySecret"
            }
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "KeySecret": {
        "type": "object",
        "additionalProperties": false,
        "description": "A secret an app's keys are signed with, without its value: the value is in no answer but the one that made it.",
        "required": [
          "id",
          "object",
          "app_id",
          "prefix",
          "created_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^ksec-[0-9a-hjkmnp-tv-z]{16}$"
          },
          "object": {
            "type": "string",
            "const": "key_secret"
          },
          "app_id": {
            "type": "string",
            "description": "The app whose keys it signs."
          },
          "prefix": {
            "type": "string",
            "description": "The secret's first 11 characters (`ks_` and 8 more), by which a list tells the two apart."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "TopupCreate": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "amount_usd"
        ],
        "properties": {
          "amount_usd": {
            "type": "number",
            "description": "US dollars in whole cents, from the smallest top-up ($20 as the control plane is started by default) to $1,000,000."
          },
          "start_now": {
            "type": "boolean",
            "description": "`true`: the account asks for what it buys to be provided at once, within the 14 days in which a consumer in the European Economic Area or the United Kingdom may withdraw from it, and acknowledges that it pays for what was provided if it withdraws and that a top-up whose Credits are all used has been provided in full and can no longer be withdrawn from (`GET /v1/billing/withdrawals`, `right.start_now`, has the words). Required of an account that buys as a consumer there (no tax id that is verified or being verified, and its billing address there, or, without one, the address the call comes from): 400 `start_now_required` without it. Kept with the purchase wherever it is given: without it, a withdrawal gives back all that was paid (control/README.md, 撤回)."
          }
        }
      },
      "Charge": {
        "type": "object",
        "additionalProperties": false,
        "description": "A charge: what whoever collects payments is asked to take from the account's card, once. A top-up, which is Credits in the account's balance when it is collected; or the fee of a period of the account's subscription.",
        "required": [
          "id",
          "object",
          "account_id",
          "period",
          "kind",
          "plan",
          "state",
          "amount_usd",
          "plan_fee_usd",
          "credit_usd",
          "debt_usd",
          "debt_id",
          "failure",
          "created_at",
          "answered_at",
          "covers",
          "refunded_at",
          "refund_reason",
          "invoice_id",
          "invoice_url",
          "invoice_pdf",
          "tax"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "object": {
            "type": "string",
            "const": "charge"
          },
          "account_id": {
            "type": "string"
          },
          "period": {
            "type": "string",
            "description": "The month it was made in, `YYYY-MM` (UTC): what auto reload takes is counted by the month."
          },
          "kind": {
            "type": "string",
            "enum": [
              "topup",
              "reload",
              "subscription"
            ],
            "x-extensible-enum": true,
            "description": "`topup`: a top-up the account asked for, or one made on its billing day of what it owes (`debt_id`). `reload`: a top-up auto reload made. `subscription`: the fee of a period of the subscription."
          },
          "plan": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "paid",
              null
            ],
            "x-extensible-enum": true,
            "description": "Of a subscription's charge: the plan it pays a period of. null for a top-up."
          },
          "state": {
            "type": "string",
            "enum": [
              "to_collect",
              "collected",
              "failed",
              "refunded"
            ],
            "x-extensible-enum": true,
            "description": "`to_collect`: it is to be collected, or is being collected. `collected`: a top-up is Credits in the account's ledger, and a subscription's charge has paid for a period. `failed`: nothing was taken, and nothing granted. `refunded`: a top-up that was collected and then taken back (`refunded_at`, `refund_reason`): what was left of its Credits was taken from the balance."
          },
          "amount_usd": {
            "type": "number",
            "description": "What is collected, in whole cents. Collected, a top-up grants as many Credits. Of a subscription's charge: the plan's fee, and what it carries of what the account owed (`debt_usd`)."
          },
          "plan_fee_usd": {
            "type": [
              "number",
              "null"
            ],
            "description": "Of a subscription's charge: the fee of the plan it pays a period of. null for a top-up."
          },
          "credit_usd": {
            "type": [
              "number",
              "null"
            ],
            "description": "Of a subscription's charge: what was taken off the plan's fee before the charge was made, which is nothing since there is one paid plan (an account moved up from a period it had paid for when there were several); 0 for any other. null for a top-up."
          },
          "debt_usd": {
            "type": [
              "number",
              "null"
            ],
            "description": "Of a subscription's charge: what the account owed when it was made, less than a debt is (`--debt-charge-least`), in whole cents rounded up, which it collects with the fee; collected, that is Credits, which pay what is owed first. 0 for one that carries nothing (nothing was owed, a debt was, or a top-up of the account was on its way). null for a top-up."
          },
          "debt_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Of a top-up made on the account's billing day of what it owes: the debt (`GET /v1/billing`, `debt.last_charge`). null for any other."
          },
          "failure": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": false,
            "required": [
              "code",
              "message"
            ],
            "properties": {
              "code": {
                "type": "string"
              },
              "message": {
                "type": "string"
              }
            },
            "description": "That it failed; null unless `failed`. To the account, never why (the reason a card's bank or Stripe gives helps whoever tries stolen cards): `card_declined`, \"The card was declined.\" (its card was refused: by its bank, by Stripe, or as the card of as many accounts as one may pay for), or `payment_failed`, \"The payment failed.\" (anything else: no card on record, a payment page that ended unpaid, an invoice voided at Stripe, Stripe out of reach or refusing the request). What to do is the same for both: pay on a payment page (`POST /v1/billing/checkout`), whose card is the account's from then on. To the operator (`GET /internal/charges`), why, as whoever collects says it. Where Stripe collects: the decline code of the card's bank, or Stripe's own code, in Stripe's words; `no_payment_method` (the account has no card); `authentication_required` (the card's bank asks for the payment to be authenticated, and nobody was there to do it: it is paid on a payment page); `invoice_voided`, `invoice_uncollectible` (its invoice was voided, or marked uncollectible, at Stripe before it was paid). Of a charge paid on a payment page: `checkout_expired` (the page ended unpaid, or was never made), `card_used_elsewhere` (its card pays for as many accounts as one may; what was paid was refunded), `payment_failed` (what was paid on it did not come in), `stripe_unavailable`, `stripe_refused` or Stripe's own code (no page was made). Of what stands in for a collector: `stand_in`."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "answered_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When it was collected or failed."
          },
          "covers": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": false,
            "required": [
              "from",
              "to"
            ],
            "properties": {
              "from": {
                "type": "string",
                "format": "date-time"
              },
              "to": {
                "type": "string",
                "format": "date-time"
              }
            },
            "description": "null for a top-up. Of a subscription's charge: the period it paid for, as it was when the charge was collected (null until then for the charge of a period that begins when it is collected: the first of a subscription)."
          },
          "refunded_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When it was taken back; null unless `refunded`."
          },
          "refund_reason": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "refund",
              "dispute",
              "fraud",
              "withdrawal",
              null
            ],
            "x-extensible-enum": true,
            "description": "Why it was taken back; null unless `refunded`. `withdrawal`: its buyer, a consumer, withdrew from it (`GET /v1/billing/withdrawals`). To the account a fraud is a `refund`, which it is (the money went back to the card): that a payment was found to be made with a stolen card is the operator's to know."
          },
          "invoice_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Where Stripe collects: Stripe's invoice of the charge, made once Stripe is asked for it (by the collector; of a charge paid on a payment page, once the page is paid). It is kept when the charge fails: an invoice whose payment failed is voided. null for a charge with none (one what stands in for a collector took, one whose invoice is not made yet)."
          },
          "invoice_url": {
            "type": [
              "string",
              "null"
            ],
            "description": "The invoice's page at Stripe (its `hosted_invoice_url`), as Stripe last gave it: Stripe's links last 30 days after the invoice is finalized. null where there is no invoice, or no page yet."
          },
          "invoice_pdf": {
            "type": [
              "string",
              "null"
            ],
            "description": "The invoice's PDF at Stripe (its `invoice_pdf`), as Stripe last gave it, and lasting as long as its page. null where there is no invoice, or no PDF yet."
          },
          "tax": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/ChargeTax"
              },
              {
                "type": "null"
              }
            ],
            "description": "What was added to it for tax and paid in all, where for, and whether it was reverse charged; null until it is collected and what it was taxed as is kept, which is at once after its answer. `amount_usd` is before tax: a collected top-up is as many Credits."
          },
          "idempotency_key": {
            "type": "string",
            "description": "Shown to the operator only. Made with the charge and never changed: what it is collected under, however often it is given to whoever collects."
          },
          "given_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Shown to the operator only: when it was last given to whoever collects."
          },
          "stripe_refund": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/StripeRefund"
              },
              {
                "type": "null"
              }
            ],
            "description": "Shown to the operator only: what was given back at Stripe of a charge that was taken back; null where nothing was (a dispute, a charge of what stands in for a collector, one that was not taken back)."
          }
        }
      },
      "ChargeTax": {
        "type": "object",
        "additionalProperties": false,
        "description": "What a charge that was collected was taxed as (control/README.md, 税).",
        "required": [
          "amount_usd",
          "total_usd",
          "country",
          "registration",
          "taxability",
          "reverse_charge"
        ],
        "properties": {
          "amount_usd": {
            "type": "number",
            "description": "The tax: 0 where none was added (not registered there, reverse charged, nobody computing it)."
          },
          "total_usd": {
            "type": "number",
            "description": "What the card paid: the charge's amount and the tax."
          },
          "country": {
            "type": [
              "string",
              "null"
            ],
            "description": "Where the tax was computed for."
          },
          "registration": {
            "type": [
              "string",
              "null"
            ],
            "description": "The registration it falls under (`au`, `eu-oss`, `gb`, ...); null: none."
          },
          "taxability": {
            "type": [
              "string",
              "null"
            ],
            "x-extensible-enum": [
              "standard_rated",
              "reduced_rated",
              "zero_rated",
              "reverse_charge",
              "not_collecting",
              "not_supported",
              "not_subject_to_tax",
              "customer_exempt",
              "product_exempt",
              "product_exempt_holiday",
              "portion_standard_rated",
              "portion_reduced_rated",
              "portion_product_exempt",
              "proportionally_rated",
              "taxable_basis_reduced",
              "excluded_territory",
              "jurisdiction_unsupported",
              "vat_exempt"
            ],
            "description": "Stripe's `taxability_reason`; null where nobody computed it."
          },
          "reverse_charge": {
            "type": "boolean",
            "description": "A business with a verified tax id where the platform is registered: no tax, and the invoice says it is reverse charged."
          }
        }
      },
      "StripeRefund": {
        "type": "object",
        "additionalProperties": false,
        "description": "What was given back at Stripe of a charge that was taken back, or of a payment page's charge that failed for its card (`stripe_refund` of a charge, to the operator).",
        "required": [
          "why",
          "invoice_id",
          "credit_note_id",
          "credit_note_pdf",
          "refund_id",
          "amount_usd",
          "made_at"
        ],
        "properties": {
          "why": {
            "type": "string",
            "enum": [
              "refund",
              "fraud",
              "card_used_elsewhere"
            ],
            "x-extensible-enum": true,
            "description": "`refund`, `fraud`: the charge was taken back. `card_used_elsewhere`: a payment page was paid with a card that pays for as many accounts as one card may, and its charge failed: what was paid was given back."
          },
          "invoice_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "The invoice it was of; null where it had none Stripe would credit."
          },
          "credit_note_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "The credit note of the invoice, by which Stripe gave the money back; null where none was made (no invoice, or it was credited before)."
          },
          "credit_note_pdf": {
            "type": [
              "string",
              "null"
            ]
          },
          "refund_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "The refund Stripe made: of the credit note, or of the PaymentIntent of a charge with no invoice."
          },
          "amount_usd": {
            "type": "number",
            "description": "What Stripe says it gave back."
          },
          "made_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "CheckoutCreate": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "purpose"
        ],
        "properties": {
          "purpose": {
            "type": "string",
            "enum": [
              "topup",
              "subscription"
            ],
            "description": "`topup`: a top-up of `amount_usd`. `subscription`: what asking for `plan` charges."
          },
          "amount_usd": {
            "type": "number",
            "description": "Of a top-up, and only of one: US dollars in whole cents, as `POST /v1/billing/topups` takes them."
          },
          "plan": {
            "type": "string",
            "enum": [
              "paid"
            ],
            "description": "Of a subscription, and only of one."
          },
          "start_now": {
            "type": "boolean",
            "description": "`true`: the account asks for what it buys to be provided at once, within the 14 days in which a consumer in the European Economic Area or the United Kingdom may withdraw from it, and acknowledges that it pays for what was provided if it withdraws and that a top-up whose Credits are all used has been provided in full and can no longer be withdrawn from (`GET /v1/billing/withdrawals`, `right.start_now`, has the words). Required of an account that buys as a consumer there (no tax id that is verified or being verified, and its billing address there, or, without one, the address the call comes from): 400 `start_now_required` without it. Kept with the purchase wherever it is given: without it, a withdrawal gives back all that was paid (control/README.md, 撤回)."
          }
        }
      },
      "Checkout": {
        "type": "object",
        "additionalProperties": false,
        "description": "A payment page of Stripe's for a charge of the account's.",
        "required": [
          "object",
          "url",
          "charge_id",
          "expires_at"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "checkout"
          },
          "url": {
            "type": "string",
            "description": "The page, at checkout.stripe.com: where the account is sent to pay."
          },
          "charge_id": {
            "type": "string",
            "description": "The charge it pays (`GET /v1/billing/charges`)."
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the page ends; unpaid by then, its charge fails with `checkout_expired`."
          }
        }
      },
      "WithdrawalList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "right",
          "withdrawable",
          "data",
          "next_cursor"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "withdrawals"
          },
          "right": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "country",
              "business",
              "applies",
              "start_now",
              "auto_reload_start_now"
            ],
            "description": "Where the account buys from now (its billing address, or the country of the address the call comes from) and whether it buys as a business: whether a purchase it makes now gives it the right to withdraw, and needs `start_now`.",
            "properties": {
              "country": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "business": {
                "type": "boolean"
              },
              "applies": {
                "type": "boolean"
              },
              "start_now": {
                "type": "string",
                "description": "The words of the request to be provided at once that a purchase carries."
              },
              "auto_reload_start_now": {
                "type": "boolean",
                "description": "Whether the account's auto reload carries the request (`PATCH /v1/billing/balance` with `start_now`, as it was turned on). Where the right applies, one turned on without it buys nothing (`auto_reload.no_request`) until it is turned on again with it."
              }
            }
          },
          "withdrawable": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WithdrawalOffer"
            }
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Withdrawal"
            }
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "WithdrawalOffer": {
        "type": "object",
        "additionalProperties": false,
        "description": "A purchase the account may withdraw from now, and what withdrawing from it would give back.",
        "required": [
          "charge_id",
          "kind",
          "plan",
          "contract",
          "bought_at",
          "withdraw_by",
          "paid_usd",
          "start_now",
          "rule",
          "refund_usd",
          "kept_usd",
          "credits_left",
          "period_share",
          "credit_share"
        ],
        "properties": {
          "charge_id": {
            "type": "string"
          },
          "kind": {
            "type": "string",
            "enum": [
              "topup",
              "reload",
              "subscription"
            ],
            "x-extensible-enum": true
          },
          "plan": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "paid",
              null
            ],
            "x-extensible-enum": true,
            "description": "Of a plan's charge, the plan; null of a top-up."
          },
          "contract": {
            "type": "string",
            "description": "What was bought, in words: `a top-up of $20.00`, `an auto reload of $20.00`, `the Paid plan, a month from 2026-11-10 08:00 UTC`."
          },
          "bought_at": {
            "type": "string",
            "format": "date-time",
            "description": "When it was bought: collected, or its period began."
          },
          "withdraw_by": {
            "type": "string",
            "format": "date-time",
            "description": "Until when a statement of withdrawal is taken for it: fifteen days after `bought_at`."
          },
          "paid_usd": {
            "type": "number",
            "description": "What was paid for it before tax (of a plan's charge, the fee less what was taken off it, without what it carried of a debt)."
          },
          "start_now": {
            "type": "boolean",
            "description": "Its buyer asked for it to be provided at once."
          },
          "rule": {
            "type": "string",
            "enum": [
              "in_full",
              "credits_used",
              "share"
            ],
            "x-extensible-enum": true,
            "description": "How what is given back is reckoned: `in_full`, all that was paid (its buyer did not ask for it at once); `credits_used`, a top-up's Credits that are left, rounded up to the cent; `share`, what was paid less the share of it the larger of `period_share` and `credit_share` comes to, rounded down to the cent."
          },
          "refund_usd": {
            "type": "number",
            "description": "What withdrawing now gives back, before tax; the tax that was charged on it is given back with it."
          },
          "kept_usd": {
            "type": "number",
            "description": "What is kept of what was paid for what was provided until now."
          },
          "credits_left": {
            "type": [
              "number",
              "null"
            ],
            "description": "Of a top-up, the Credits left of it, which withdrawing takes from the balance; null of a plan's charge."
          },
          "period_share": {
            "type": [
              "number",
              "null"
            ],
            "minimum": 0,
            "maximum": 1,
            "description": "Of a plan's charge, the part of its period that has gone by; null of a top-up."
          },
          "credit_share": {
            "type": [
              "number",
              "null"
            ],
            "minimum": 0,
            "maximum": 1,
            "description": "Of a plan: the largest share used of what its period includes of a meter (the meter used most), from 0 to 1; the larger of it and `period_share` is the share of the fee that is kept. null of a top-up."
          }
        }
      },
      "Withdrawal": {
        "type": "object",
        "additionalProperties": false,
        "description": "A withdrawal from a purchase: the statement as it was received, what was kept and is given back, and how giving it back and the acknowledgement stand.",
        "required": [
          "object",
          "id",
          "account_id",
          "charge_id",
          "kind",
          "plan",
          "contract",
          "bought_at",
          "submitted_at",
          "name",
          "email",
          "statement",
          "submitted_by",
          "start_now",
          "rule",
          "paid_usd",
          "kept_usd",
          "refund_usd",
          "credits_taken",
          "period_share",
          "credit_share",
          "plan_ended",
          "auto_reload_off",
          "refund",
          "confirmation"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "withdrawal"
          },
          "id": {
            "type": "string",
            "pattern": "^wdr-[0-9a-hjkmnp-tv-z]{16}$"
          },
          "account_id": {
            "type": "string",
            "pattern": "^acct-[0-9a-hjkmnp-tv-z]{16}$"
          },
          "charge_id": {
            "type": "string"
          },
          "kind": {
            "type": "string",
            "enum": [
              "topup",
              "reload",
              "subscription"
            ],
            "x-extensible-enum": true
          },
          "plan": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "paid",
              null
            ],
            "x-extensible-enum": true,
            "description": "Of a plan's charge, the plan; null of a top-up."
          },
          "contract": {
            "type": "string",
            "description": "What was withdrawn from, in words."
          },
          "bought_at": {
            "type": "string",
            "format": "date-time"
          },
          "submitted_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the statement was received: the date and time the acknowledgement says."
          },
          "name": {
            "type": "string"
          },
          "email": {
            "type": "string",
            "description": "Where the acknowledgement of receipt goes."
          },
          "statement": {
            "type": "string",
            "description": "The statement, in the words of the terms' model form, as it was received and acknowledged."
          },
          "submitted_by": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "name"
            ],
            "description": "Who sent it: a user of the console (its id and address), or a key (its id and name).",
            "properties": {
              "id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "name": {
                "type": [
                  "string",
                  "null"
                ]
              }
            }
          },
          "start_now": {
            "type": "boolean",
            "description": "Its buyer asked for the purchase to be provided at once."
          },
          "rule": {
            "type": "string",
            "enum": [
              "in_full",
              "credits_used",
              "share"
            ],
            "x-extensible-enum": true,
            "description": "How what is given back is reckoned: `in_full`, all that was paid (its buyer did not ask for it at once); `credits_used`, a top-up's Credits that are left, rounded up to the cent; `share`, what was paid less the share of it the larger of `period_share` and `credit_share` comes to, rounded down to the cent."
          },
          "paid_usd": {
            "type": "number"
          },
          "kept_usd": {
            "type": "number",
            "description": "What was kept of what was paid for what had been provided, before tax."
          },
          "refund_usd": {
            "type": "number",
            "description": "What is given back, before tax; the tax that was charged on it goes back with it."
          },
          "credits_taken": {
            "type": "number",
            "description": "Of a top-up, the Credits that were left of it and were taken from the balance."
          },
          "period_share": {
            "type": [
              "number",
              "null"
            ],
            "minimum": 0,
            "maximum": 1,
            "description": "Of a plan's charge, the part of its period that had gone by when the statement was received."
          },
          "credit_share": {
            "type": [
              "number",
              "null"
            ],
            "minimum": 0,
            "maximum": 1,
            "description": "Of a plan: the largest share used of what its period includes of a meter (the meter used most), from 0 to 1; the larger of it and `period_share` is the share of the fee that is kept. null of a top-up."
          },
          "plan_ended": {
            "type": "boolean",
            "description": "The subscription ended with it, and the account is on the free plan."
          },
          "auto_reload_off": {
            "type": "boolean",
            "description": "Of a top-up: the account's auto reload was on, and was turned off with it."
          },
          "refund": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "state",
              "refunded_at",
              "total_usd"
            ],
            "properties": {
              "state": {
                "type": "string",
                "enum": [
                  "refunding",
                  "refunded",
                  "nothing"
                ],
                "x-extensible-enum": true,
                "description": "`refunding`: to be made (tried again every minute while Stripe cannot be reached); `refunded`; `nothing`: nothing is given back."
              },
              "refunded_at": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time"
              },
              "total_usd": {
                "type": [
                  "number",
                  "null"
                ],
                "description": "What went back to the card, with its tax."
              }
            }
          },
          "confirmation": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "to",
              "state",
              "sent_at"
            ],
            "description": "The acknowledgement of receipt, by mail.",
            "properties": {
              "to": {
                "type": "string"
              },
              "state": {
                "type": [
                  "string",
                  "null"
                ],
                "enum": [
                  "to_send",
                  "accepted",
                  "failed",
                  "cancelled",
                  null
                ],
                "x-extensible-enum": true,
                "description": "As its notice stands (one that failed is sent again)."
              },
              "sent_at": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time"
              }
            }
          }
        }
      },
      "WithdrawalCreate": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "charge_id",
          "name"
        ],
        "properties": {
          "charge_id": {
            "type": "string",
            "description": "The purchase withdrawn from, as `GET /v1/billing/withdrawals` lists it in `withdrawable`."
          },
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200,
            "description": "The buyer's name, as the statement gives it."
          },
          "email": {
            "type": "string",
            "description": "Where the acknowledgement of receipt is sent; the address the account's notices go to where it is left out."
          }
        }
      },
      "BillingPortal": {
        "type": "object",
        "additionalProperties": false,
        "description": "A session of Stripe's customer portal (`POST /v1/billing/portal`).",
        "required": [
          "object",
          "url",
          "return_url",
          "created_at"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "billing_portal"
          },
          "url": {
            "type": "string",
            "description": "The portal, for the account to open in a browser: its alone, for a short while (billing.stripe.com)."
          },
          "return_url": {
            "type": "string",
            "description": "Where the portal sends the customer back to: the account's billing page in the console (`<--console-url>#/a/<account id>/billing?back=portal`), or `--portal-return-url`."
          },
          "created_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "InvoiceList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "data",
          "next_cursor"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Invoice"
            }
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "Invoice": {
        "type": "object",
        "additionalProperties": false,
        "description": "An invoice of the account's at Stripe, of one of its charges (`GET /v1/billing/invoices`).",
        "required": [
          "object",
          "id",
          "charge_id",
          "kind",
          "plan",
          "number",
          "status",
          "amount_usd",
          "total_usd",
          "currency",
          "created_at",
          "invoice_url",
          "invoice_pdf",
          "links_read_at",
          "credit_note"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "invoice"
          },
          "id": {
            "type": "string",
            "description": "Stripe's id of the invoice."
          },
          "charge_id": {
            "type": "string",
            "description": "The charge it is of (`GET /v1/billing/charges`)."
          },
          "kind": {
            "type": "string",
            "enum": [
              "topup",
              "reload",
              "subscription"
            ],
            "x-extensible-enum": true,
            "description": "The charge's kind."
          },
          "plan": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "paid",
              null
            ],
            "x-extensible-enum": true,
            "description": "Of a subscription's charge, the plan it paid a period of."
          },
          "number": {
            "type": [
              "string",
              "null"
            ],
            "description": "Its number at Stripe, once it is finalized; null until it is read from Stripe."
          },
          "status": {
            "type": [
              "string",
              "null"
            ],
            "description": "`draft`, `open`, `paid`, `void` or `uncollectible`, as Stripe last said it; null until it is read from Stripe."
          },
          "amount_usd": {
            "type": "number",
            "description": "What the charge was, in whole cents."
          },
          "total_usd": {
            "type": [
              "number",
              "null"
            ],
            "description": "What the invoice comes to at Stripe, tax and all, as Stripe last said it."
          },
          "currency": {
            "type": "string",
            "const": "usd"
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the charge was made."
          },
          "invoice_url": {
            "type": [
              "string",
              "null"
            ],
            "description": "Its page at Stripe, as Stripe last gave it."
          },
          "invoice_pdf": {
            "type": [
              "string",
              "null"
            ],
            "description": "Its PDF at Stripe, as Stripe last gave it."
          },
          "links_read_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When its page and PDF were read from Stripe: they are read again when that was more than a day ago."
          },
          "credit_note": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": false,
            "required": [
              "id",
              "pdf",
              "amount_usd"
            ],
            "properties": {
              "id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "pdf": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "amount_usd": {
                "type": "number"
              }
            },
            "description": "Of a charge that was taken back: the credit note of the invoice by which Stripe gave the money back, its PDF, and what it gave back; null for any other."
          }
        }
      },
      "ChargeList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "data",
          "next_cursor"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Charge"
            }
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "AccountNoticeList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "data",
          "next_cursor"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "notices"
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AccountNotice"
            }
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "AccountNotice": {
        "type": "object",
        "additionalProperties": false,
        "description": "A notice of the account, as it lists its mail: what a debt's notices show (`DebtNotice`), and the address, the words and their language once they were made.",
        "required": [
          "id",
          "object",
          "kind",
          "state",
          "due_at",
          "sent_at",
          "subject",
          "to",
          "body",
          "language",
          "failed_at"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "object": {
            "type": "string",
            "const": "notice"
          },
          "kind": {
            "type": "string",
            "x-extensible-enum": [
              "in_debt",
              "reminder_7",
              "reminder_14",
              "reminder_21",
              "reminder_27",
              "reminder_29",
              "charge_failed",
              "data_deleted",
              "storage_80",
              "storage_100",
              "subscription_charge_failed",
              "reload_failed",
              "reload_no_card",
              "reload_no_request",
              "included_80",
              "included_100",
              "credits_expiring_30",
              "credits_expiring_7",
              "credits_expiring_1",
              "tax_address_confirm",
              "tax_payment_held",
              "withdrawal_received",
              "purchase_confirmed",
              "account_held",
              "subscription_ended",
              "verify_account_email",
              "limit_raised",
              "limit_refused"
            ],
            "description": "What it is: the kinds of `Notice` (the operator's), but the mails of the console's users; the link that verifies the account's address (`verify_account_email`) among them, its words without the link."
          },
          "state": {
            "type": "string",
            "x-extensible-enum": [
              "to_send",
              "accepted",
              "failed"
            ]
          },
          "due_at": {
            "type": "string",
            "format": "date-time",
            "description": "From when it is handed to the sender."
          },
          "sent_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the sender took it, by the books' clock; null while it is not accepted."
          },
          "subject": {
            "type": [
              "string",
              "null"
            ],
            "description": "Null until its words are made: the first time it is handed to the sender."
          },
          "to": {
            "type": [
              "string",
              "null"
            ],
            "description": "The address it went to: the account's address then, or, of the acknowledgement of a withdrawal, the one the statement gave."
          },
          "body": {
            "type": [
              "string",
              "null"
            ],
            "description": "Its words, as plain text, as they went."
          },
          "language": {
            "type": [
              "string",
              "null"
            ],
            "x-extensible-enum": [
              "en",
              "de",
              "fr"
            ],
            "description": "The language its words are in (`AccountLanguage`; a kind that has no words in the account's language is in English)."
          },
          "failed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the sender said it failed; null for one that did not."
          }
        }
      },
      "LedgerEntryList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "data",
          "next_cursor"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LedgerEntry"
            }
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "LedgerEntry": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id",
          "object",
          "account_id",
          "period",
          "kind",
          "credits",
          "open",
          "charge_id",
          "lot_id",
          "note",
          "expires_at",
          "created_at",
          "updated_at"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "object": {
            "type": "string",
            "const": "ledger_entry"
          },
          "account_id": {
            "type": "string"
          },
          "period": {
            "type": "string",
            "description": "The month it is of, `YYYY-MM` (UTC): the month a lot came in, the month usage is of, the month a lot lapsed with, the month a top-up was taken back in, the month a lapsed lot was brought back in."
          },
          "kind": {
            "type": "string",
            "enum": [
              "credit",
              "topup",
              "usage",
              "expiry",
              "refund",
              "debt_write_off",
              "restore",
              "month_grant",
              "period_grant"
            ],
            "x-extensible-enum": true,
            "description": "Lots: `credit` (what the operator granted), `topup` (a top-up that was collected), and, only in a store from before plans included quantities, `month_grant` and `period_grant` (Credits a calendar month or a period gave). `usage`: what a month's usage past what the plan includes comes to, as far as lots paid it or it is owed: one entry a month. `expiry`: what was left of a lot when it lapsed. `refund`: what was left of a top-up's lot when the top-up was taken back. `debt_write_off`: what an account owed when what it stored had been deleted for it, which is owed no longer: it pays what was owed as a lot does, is nobody's Credits, and its `note` is the debt's id. `restore`: what an operator brought back of a lot that lapsed (`POST /internal/accounts/{id}/credits/restore`), which adds what was written off then; the lot is open again."
          },
          "credits": {
            "type": "number",
            "description": "Above zero it adds to the balance (a lot: what was granted), below zero it takes from it."
          },
          "open": {
            "type": "boolean",
            "description": "Of `usage`: the entry is still brought up to its month's statement (the month is not closed). Of a lot: it has not lapsed, or has in the last seconds and waits for what is left of it to be written off. Of `expiry`, `refund` and `restore`: false."
          },
          "charge_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Of `topup`: the top-up that was collected."
          },
          "lot_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Of `expiry` and `refund`: the lot whose rest this is. Of `restore`: the lot brought back."
          },
          "note": {
            "type": [
              "string",
              "null"
            ],
            "description": "Of `credit`: what the operator granted it for. Of `restore`: why the operator brought it back."
          },
          "expires_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Of a lot: when it lapses. Of `restore`: when the lot brought back lapses from then."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "description": "Of `usage`: when the entry was last brought up to its statement."
          }
        }
      },
      "KeyCreate": {
        "type": "object",
        "additionalProperties": false,
        "description": "Every field may be left out.",
        "properties": {
          "name": {
            "type": "string",
            "maxLength": 100,
            "description": "At most 100 characters, none of them a control character."
          },
          "scopes": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "read",
                "write",
                "exec",
                "files",
                "tty",
                "url",
                "admin"
              ]
            },
            "description": "What the key may do. Left out: every scope but `admin`."
          },
          "expires_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "A time in the future, from which the key is refused; null or left out: it does not run out."
          },
          "installation": {
            "type": [
              "string",
              "null"
            ],
            "description": "One of the project's installations of a Managed Application (its id or name) the key is held to: its calls reach that installation (to read it), its apps and what is under them, its Drives and its instances, and nothing else of the project (403 `installation_key_scope`); it is not allowed `admin`, and is revoked when the installation is deleted. What a controller of the app acts with."
          }
        }
      },
      "KeyMade": {
        "type": "object",
        "additionalProperties": false,
        "description": "A key that was just made, with the key itself: it is shown this once, and the store keeps its SHA-256 only.",
        "required": [
          "id",
          "object",
          "name",
          "prefix",
          "scopes",
          "created_at",
          "expires_at",
          "last_used_at",
          "installation_id",
          "key"
        ],
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^key-[0-9a-hjkmnp-tv-z]{16}$"
          },
          "object": {
            "type": "string",
            "const": "key"
          },
          "name": {
            "type": "string",
            "maxLength": 100,
            "description": "What the key is called: empty where it was made without a name."
          },
          "prefix": {
            "type": "string",
            "description": "The key's first 11 characters (`ak_` and 8 more), by which a list tells keys apart; of the development key (`--dev-key-file`), its first 3."
          },
          "scopes": {
            "type": "array",
            "items": {
              "type": "string",
              "x-extensible-enum": [
                "read",
                "write",
                "exec",
                "files",
                "tty",
                "url",
                "admin"
              ]
            },
            "description": "What the key may do: each call names the scope it takes."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "expires_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "From this time on the key is refused (401 `expired_api_key`). Null: it does not run out."
          },
          "last_used_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When a call last came with it, to the half minute. Null: none has."
          },
          "installation_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "The installation the key is held to; null: the whole project."
          },
          "key": {
            "type": "string",
            "description": "`ak_` and 32 characters (160 random bits): what a call gives as `Authorization: Bearer <key>`."
          }
        }
      },
      "KeyList": {
        "type": "object",
        "additionalProperties": false,
        "description": "The project's keys, the newest first.",
        "required": [
          "data",
          "next_cursor"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Key"
            }
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Where the next page begins; null on the last."
          }
        }
      },
      "Key": {
        "type": "object",
        "additionalProperties": false,
        "description": "A key of the project. The key itself is in no answer but the one that made it.",
        "required": [
          "id",
          "object",
          "name",
          "prefix",
          "scopes",
          "created_at",
          "expires_at",
          "last_used_at",
          "installation_id"
        ],
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^key-[0-9a-hjkmnp-tv-z]{16}$"
          },
          "object": {
            "type": "string",
            "const": "key"
          },
          "name": {
            "type": "string",
            "maxLength": 100,
            "description": "What the key is called: empty where it was made without a name."
          },
          "prefix": {
            "type": "string",
            "description": "The key's first 11 characters (`ak_` and 8 more), by which a list tells keys apart; of the development key (`--dev-key-file`), its first 3."
          },
          "scopes": {
            "type": "array",
            "items": {
              "type": "string",
              "x-extensible-enum": [
                "read",
                "write",
                "exec",
                "files",
                "tty",
                "url",
                "admin"
              ]
            },
            "description": "What the key may do: each call names the scope it takes."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "expires_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "From this time on the key is refused (401 `expired_api_key`). Null: it does not run out."
          },
          "last_used_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When a call last came with it, to the half minute. Null: none has."
          },
          "installation_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "The installation of a Managed Application the key is held to (`installation` of its create): it reaches that installation's own things alone. Null: the whole project."
          }
        }
      },
      "Limits": {
        "type": "object",
        "additionalProperties": false,
        "description": "A project's spend limit: when what the project used in a calendar month comes to it, every sandbox of the project is frozen and nothing new is made in it (403 `spend_limit_reached`), until the limit is raised or taken away or the month is over. What volumes and snapshots keep cannot be frozen and is counted on. The control plane looks every `--usage-seconds`, so what is past the limit is those seconds' use.",
        "required": [
          "object",
          "spend_limit_usd",
          "spent_usd",
          "period",
          "frozen",
          "prices"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "limits"
          },
          "spend_limit_usd": {
            "type": [
              "number",
              "null"
            ],
            "minimum": 0,
            "description": "The limit, in US dollars for a calendar month (UTC). Null: none."
          },
          "spent_usd": {
            "type": "number",
            "minimum": 0,
            "description": "What was used in the month comes to at the month's prices: every meter, each to the nearest millionth of a dollar, added up. What the account's plan includes is in it: the limit is held against all of it."
          },
          "period": {
            "type": "string",
            "pattern": "^[0-9]{4}-[0-9]{2}$",
            "description": "The calendar month."
          },
          "frozen": {
            "type": "boolean",
            "description": "Whether the limit is reached and holds what it is for frozen: asleep with its data, and woken by nothing (`spend_limit_reached`)."
          },
          "prices": {
            "$ref": "#/components/schemas/Prices",
            "description": "What the month is priced at: the prices it began with."
          }
        }
      },
      "LimitsUpdate": {
        "type": "object",
        "additionalProperties": false,
        "description": "Left out, nothing changes.",
        "properties": {
          "spend_limit_usd": {
            "type": [
              "number",
              "null"
            ],
            "minimum": 0,
            "description": "US dollars, zero or more, to the nearest millionth; null takes the limit away."
          }
        }
      },
      "LimitRequestList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "data",
          "next_cursor"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "list"
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LimitRequest"
            }
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "LimitRequest": {
        "type": "object",
        "additionalProperties": false,
        "description": "An account's request to have one of its limits raised (`POST /v1/limits/requests`), and the operator's answer.",
        "required": [
          "id",
          "object",
          "account_id",
          "limit",
          "from",
          "wanted",
          "why",
          "status",
          "granted",
          "answer",
          "created_at",
          "answered_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^lrq-[0-9a-hjkmnp-tv-z]{16}$"
          },
          "object": {
            "type": "string",
            "const": "limit_request"
          },
          "account_id": {
            "type": "string",
            "pattern": "^acct-[0-9a-hjkmnp-tv-z]{16}$"
          },
          "limit": {
            "type": "string",
            "enum": [
              "instances",
              "vcpus",
              "memory_bytes",
              "sleeping",
              "app_instances",
              "creates_per_minute",
              "creates_per_hour_per_project",
              "app_creates_per_hour",
              "volumes",
              "volume_bytes"
            ],
            "description": "Which limit: one of the eight caps of what the account has at once and makes in a while (`GET /v1/account`, `caps`), or what a project keeps of Drives and volumes: how many (`volumes`) and how large one may be (`volume_bytes`)."
          },
          "from": {
            "type": "integer",
            "minimum": 0,
            "description": "The account's number of the limit when it asked: its plan's, or one an operator gave it. Of `memory_bytes` and `volume_bytes`, bytes."
          },
          "wanted": {
            "type": "integer",
            "minimum": 1,
            "description": "The number asked for, in the limit's units."
          },
          "why": {
            "type": "string",
            "description": "What the account needs it for, as it said it."
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "granted",
              "refused"
            ],
            "description": "`pending`: it waits for the operator's answer; `granted`: the account has the number `granted` from `answered_at`; `refused`: nothing changed, and `answer` says why."
          },
          "granted": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 1,
            "description": "The number the account was given, as one of its own; null while pending, and of a refusal."
          },
          "answer": {
            "type": [
              "string",
              "null"
            ],
            "description": "What the operator said with the answer: why, of a refusal; null where nothing was said."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "answered_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "asked_by": {
            "type": "string",
            "description": "The operator's alone (`/internal/limits/requests`): the id of the key the request was made with, or of the console's user whose session made it."
          }
        }
      },
      "LimitRequestCreate": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "limit",
          "wanted",
          "why"
        ],
        "properties": {
          "limit": {
            "type": "string",
            "enum": [
              "instances",
              "vcpus",
              "memory_bytes",
              "sleeping",
              "app_instances",
              "creates_per_minute",
              "creates_per_hour_per_project",
              "app_creates_per_hour",
              "volumes",
              "volume_bytes"
            ],
            "description": "The limit to raise."
          },
          "wanted": {
            "type": "integer",
            "minimum": 1,
            "description": "The number the account needs, above its number now; of `memory_bytes` and `volume_bytes`, bytes (`volume_bytes` 16 TiB at the most)."
          },
          "why": {
            "type": "string",
            "minLength": 1,
            "maxLength": 2000,
            "description": "What the account needs it for: 1 to 2,000 characters."
          }
        }
      },
      "SandboxLimits": {
        "type": "object",
        "additionalProperties": false,
        "description": "One sandbox's own spend limit: at it that sandbox alone is frozen. What volumes and snapshots keep is the project's, and in no sandbox's spend.",
        "required": [
          "object",
          "spend_limit_usd",
          "spent_usd",
          "period",
          "frozen",
          "sandbox_id"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "limits"
          },
          "spend_limit_usd": {
            "type": [
              "number",
              "null"
            ],
            "minimum": 0,
            "description": "The limit, in US dollars for a calendar month (UTC). Null: none."
          },
          "spent_usd": {
            "type": "number",
            "minimum": 0,
            "description": "What was used in the month comes to at the month's prices: every meter, each to the nearest millionth of a dollar, added up. What the account's plan includes is in it: the limit is held against all of it."
          },
          "period": {
            "type": "string",
            "pattern": "^[0-9]{4}-[0-9]{2}$",
            "description": "The calendar month."
          },
          "frozen": {
            "type": "boolean",
            "description": "Whether the limit is reached and holds what it is for frozen: asleep with its data, and woken by nothing (`spend_limit_reached`)."
          },
          "sandbox_id": {
            "type": "string",
            "pattern": "^sbx-[0-9a-hjkmnp-tv-z]{16}$"
          }
        }
      },
      "EventList": {
        "type": "object",
        "additionalProperties": false,
        "description": "The project's events, the newest first.",
        "required": [
          "data",
          "next_cursor"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Event"
            }
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Where the next page begins; null on the last."
          }
        }
      },
      "Event": {
        "type": "object",
        "additionalProperties": false,
        "description": "Something that happened to a thing of the project, or to its account. `x-events` has every type, when it comes and the shape of its `data`.",
        "required": [
          "id",
          "object",
          "type",
          "created_at",
          "data"
        ],
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^evt-[0-9a-hjkmnp-tv-z]{16}$"
          },
          "object": {
            "type": "string",
            "const": "event"
          },
          "type": {
            "type": "string",
            "x-extensible-enum": [
              "sandbox.created",
              "sandbox.deleted",
              "sandbox.restarted",
              "sandbox.slept",
              "sandbox.woke",
              "sandbox.moved",
              "sandbox.move_failed",
              "sandbox.hydrated",
              "sandbox.tokens_revoked",
              "sandbox.disk_limited",
              "browser.failed",
              "spend_limit.reached",
              "egress_overage_cap.reached",
              "subscription.started",
              "subscription.renewed",
              "subscription.changed",
              "subscription.charge_failed",
              "subscription.ended",
              "topup.collected",
              "topup.failed",
              "topup.refunded",
              "subscription.refunded",
              "withdrawal.received",
              "withdrawal.refunded",
              "balance.used_up",
              "balance.restored",
              "credits.expired",
              "storage_included.reached",
              "included.reached",
              "trust_limit.reached",
              "trust_level.changed",
              "limit_request.granted",
              "limit_request.refused",
              "auto_reload.no_card",
              "auto_reload.no_request",
              "account.in_debt",
              "account.debt_reminder",
              "account.debt_charge_failed",
              "account.debt_paid",
              "account.data_deleted",
              "account.held",
              "account.released",
              "tax.address_to_confirm",
              "tax.payment_held",
              "tax_id.updated",
              "tax_region.business_only",
              "domain.active",
              "domain.detached",
              "domain.lost",
              "domain.renewal_stopped",
              "domain.renewal_resumed",
              "app.deployed",
              "app.deploy_failed",
              "app.build_failed",
              "destination.failing",
              "destination.recovered",
              "database.archive_failing",
              "database.archive_recovered",
              "database.history_gap",
              "database.history_resumed",
              "project.deployed",
              "project.deploy_failed",
              "project.key_destroyed",
              "project.key_unrecoverable",
              "credits.expiring",
              "credits.restored",
              "backup.made",
              "backup.failed",
              "backup.deleted",
              "drive.created",
              "drive.deleted",
              "drive.attached",
              "drive.detached",
              "drive.snapshot.created",
              "drive.snapshot.deleted",
              "database.created",
              "database.deleted",
              "database.branch_created",
              "database.branch_deleted",
              "database.restored",
              "key.created",
              "key.deleted",
              "instance.upgraded",
              "instance.upgrade_failed",
              "instance.reverted",
              "instance.restarted",
              "installation.created",
              "installation.ready",
              "installation.failed",
              "installation.updated",
              "installation.deleted"
            ],
            "description": "Open: a type that is added later is sent to a webhook that takes every type, and a receiver lets pass what it does not know."
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the control plane recorded it."
          },
          "data": {
            "type": "object",
            "additionalProperties": true,
            "description": "What the event is about: the schema `x-events` names for its type."
          }
        }
      },
      "WebhookCreate": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "url"
        ],
        "properties": {
          "url": {
            "type": "string",
            "maxLength": 2048,
            "description": "`http://` or `https://`, with a host, at most 2048 characters. An address that resolves to none on the public internet (this machine, a private or link-local network, a reserved range) is not delivered to, unless the control plane is started so (`--webhook-allow-private true`, for development)."
          },
          "events": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "string",
              "enum": [
                "sandbox.created",
                "sandbox.deleted",
                "sandbox.restarted",
                "sandbox.slept",
                "sandbox.woke",
                "sandbox.moved",
                "sandbox.move_failed",
                "sandbox.hydrated",
                "sandbox.tokens_revoked",
                "sandbox.disk_limited",
                "browser.failed",
                "spend_limit.reached",
                "egress_overage_cap.reached",
                "subscription.started",
                "subscription.renewed",
                "subscription.changed",
                "subscription.charge_failed",
                "subscription.ended",
                "topup.collected",
                "topup.failed",
                "topup.refunded",
                "subscription.refunded",
                "withdrawal.received",
                "withdrawal.refunded",
                "balance.used_up",
                "balance.restored",
                "credits.expired",
                "storage_included.reached",
                "included.reached",
                "trust_limit.reached",
                "trust_level.changed",
                "limit_request.granted",
                "limit_request.refused",
                "auto_reload.no_card",
                "auto_reload.no_request",
                "account.in_debt",
                "account.debt_reminder",
                "account.debt_charge_failed",
                "account.debt_paid",
                "account.data_deleted",
                "account.held",
                "account.released",
                "tax.address_to_confirm",
                "tax.payment_held",
                "tax_id.updated",
                "tax_region.business_only",
                "domain.active",
                "domain.detached",
                "domain.lost",
                "domain.renewal_stopped",
                "domain.renewal_resumed",
                "app.deployed",
                "app.deploy_failed",
                "app.build_failed",
                "destination.failing",
                "destination.recovered",
                "database.archive_failing",
                "database.archive_recovered",
                "database.history_gap",
                "database.history_resumed",
                "project.deployed",
                "project.deploy_failed",
                "project.key_destroyed",
                "project.key_unrecoverable",
                "credits.expiring",
                "credits.restored",
                "backup.made",
                "backup.failed",
                "backup.deleted",
                "drive.created",
                "drive.deleted",
                "drive.attached",
                "drive.detached",
                "drive.snapshot.created",
                "drive.snapshot.deleted",
                "database.created",
                "database.deleted",
                "database.branch_created",
                "database.branch_deleted",
                "database.restored",
                "key.created",
                "key.deleted",
                "instance.upgraded",
                "instance.upgrade_failed",
                "instance.reverted",
                "instance.restarted",
                "installation.created",
                "installation.ready",
                "installation.failed",
                "installation.updated",
                "installation.deleted"
              ]
            },
            "description": "The types it takes; left out, null or empty: every type."
          },
          "enabled": {
            "type": "boolean",
            "default": true
          }
        }
      },
      "WebhookMade": {
        "type": "object",
        "additionalProperties": false,
        "description": "A webhook that was just made, with its secret: shown this once.",
        "required": [
          "id",
          "object",
          "url",
          "events",
          "enabled",
          "created_at",
          "last_delivery",
          "secret"
        ],
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^wh-[0-9a-hjkmnp-tv-z]{16}$"
          },
          "object": {
            "type": "string",
            "const": "webhook"
          },
          "url": {
            "type": "string",
            "description": "Where the events are posted."
          },
          "events": {
            "type": "array",
            "items": {
              "type": "string",
              "x-extensible-enum": [
                "sandbox.created",
                "sandbox.deleted",
                "sandbox.restarted",
                "sandbox.slept",
                "sandbox.woke",
                "sandbox.moved",
                "sandbox.move_failed",
                "sandbox.hydrated",
                "sandbox.tokens_revoked",
                "sandbox.disk_limited",
                "browser.failed",
                "spend_limit.reached",
                "egress_overage_cap.reached",
                "subscription.started",
                "subscription.renewed",
                "subscription.changed",
                "subscription.charge_failed",
                "subscription.ended",
                "topup.collected",
                "topup.failed",
                "topup.refunded",
                "subscription.refunded",
                "withdrawal.received",
                "withdrawal.refunded",
                "balance.used_up",
                "balance.restored",
                "credits.expired",
                "storage_included.reached",
                "included.reached",
                "trust_limit.reached",
                "trust_level.changed",
                "limit_request.granted",
                "limit_request.refused",
                "auto_reload.no_card",
                "auto_reload.no_request",
                "account.in_debt",
                "account.debt_reminder",
                "account.debt_charge_failed",
                "account.debt_paid",
                "account.data_deleted",
                "account.held",
                "account.released",
                "tax.address_to_confirm",
                "tax.payment_held",
                "tax_id.updated",
                "tax_region.business_only",
                "domain.active",
                "domain.detached",
                "domain.lost",
                "domain.renewal_stopped",
                "domain.renewal_resumed",
                "app.deployed",
                "app.deploy_failed",
                "app.build_failed",
                "destination.failing",
                "destination.recovered",
                "database.archive_failing",
                "database.archive_recovered",
                "database.history_gap",
                "database.history_resumed",
                "project.deployed",
                "project.deploy_failed",
                "project.key_destroyed",
                "project.key_unrecoverable",
                "credits.expiring",
                "credits.restored",
                "backup.made",
                "backup.failed",
                "backup.deleted",
                "drive.created",
                "drive.deleted",
                "drive.attached",
                "drive.detached",
                "drive.snapshot.created",
                "drive.snapshot.deleted",
                "database.created",
                "database.deleted",
                "database.branch_created",
                "database.branch_deleted",
                "database.restored",
                "key.created",
                "key.deleted",
                "instance.upgraded",
                "instance.upgrade_failed",
                "instance.reverted",
                "instance.restarted",
                "installation.created",
                "installation.ready",
                "installation.failed",
                "installation.updated",
                "installation.deleted"
              ]
            },
            "description": "The types it takes; empty: every type, those added later among them."
          },
          "enabled": {
            "type": "boolean",
            "description": "Whether events are sent to it: one that is not enabled is given none of the events recorded meanwhile."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "last_delivery": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/WebhookDelivery"
              },
              {
                "type": "null"
              }
            ],
            "description": "Null: nothing has been sent to it yet."
          },
          "secret": {
            "type": "string",
            "pattern": "^whsec_[A-Za-z0-9+/]{32}$",
            "description": "`whsec_` and the base64 of 24 random bytes: the bytes are the key the deliveries are signed with."
          }
        }
      },
      "WebhookDelivery": {
        "type": "object",
        "additionalProperties": false,
        "description": "The last delivery to the webhook: of the newest event it was to be sent.",
        "required": [
          "event_id",
          "status",
          "attempts",
          "response_status",
          "error",
          "delivered_at",
          "next_attempt_at"
        ],
        "properties": {
          "event_id": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "x-extensible-enum": [
              "pending",
              "delivered",
              "failed"
            ],
            "description": "`pending`: to be sent, or to be sent again; `delivered`: the webhook answered 2xx; `failed`: it was tried as often as it is tried, and given up."
          },
          "attempts": {
            "type": "integer",
            "minimum": 0
          },
          "response_status": {
            "type": [
              "integer",
              "null"
            ],
            "description": "The status of the last answer; null when none came."
          },
          "error": {
            "type": [
              "string",
              "null"
            ],
            "description": "Why the last attempt failed: the status, or what kept the request from being made or answered."
          },
          "delivered_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When it was delivered, or given up."
          },
          "next_attempt_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When it is tried next, while it is `pending`."
          }
        }
      },
      "WebhookList": {
        "type": "object",
        "additionalProperties": false,
        "description": "The project's webhooks, the newest first.",
        "required": [
          "data",
          "next_cursor"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Webhook"
            }
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Where the next page begins; null on the last."
          }
        }
      },
      "Webhook": {
        "type": "object",
        "additionalProperties": false,
        "description": "Where a project's events are sent. The secret they are signed with is in no answer but the one that made it.",
        "required": [
          "id",
          "object",
          "url",
          "events",
          "enabled",
          "created_at",
          "last_delivery"
        ],
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^wh-[0-9a-hjkmnp-tv-z]{16}$"
          },
          "object": {
            "type": "string",
            "const": "webhook"
          },
          "url": {
            "type": "string",
            "description": "Where the events are posted."
          },
          "events": {
            "type": "array",
            "items": {
              "type": "string",
              "x-extensible-enum": [
                "sandbox.created",
                "sandbox.deleted",
                "sandbox.restarted",
                "sandbox.slept",
                "sandbox.woke",
                "sandbox.moved",
                "sandbox.move_failed",
                "sandbox.hydrated",
                "sandbox.tokens_revoked",
                "sandbox.disk_limited",
                "browser.failed",
                "spend_limit.reached",
                "egress_overage_cap.reached",
                "subscription.started",
                "subscription.renewed",
                "subscription.changed",
                "subscription.charge_failed",
                "subscription.ended",
                "topup.collected",
                "topup.failed",
                "topup.refunded",
                "subscription.refunded",
                "withdrawal.received",
                "withdrawal.refunded",
                "balance.used_up",
                "balance.restored",
                "credits.expired",
                "storage_included.reached",
                "included.reached",
                "trust_limit.reached",
                "trust_level.changed",
                "limit_request.granted",
                "limit_request.refused",
                "auto_reload.no_card",
                "auto_reload.no_request",
                "account.in_debt",
                "account.debt_reminder",
                "account.debt_charge_failed",
                "account.debt_paid",
                "account.data_deleted",
                "account.held",
                "account.released",
                "tax.address_to_confirm",
                "tax.payment_held",
                "tax_id.updated",
                "tax_region.business_only",
                "domain.active",
                "domain.detached",
                "domain.lost",
                "domain.renewal_stopped",
                "domain.renewal_resumed",
                "app.deployed",
                "app.deploy_failed",
                "app.build_failed",
                "destination.failing",
                "destination.recovered",
                "database.archive_failing",
                "database.archive_recovered",
                "database.history_gap",
                "database.history_resumed",
                "project.deployed",
                "project.deploy_failed",
                "project.key_destroyed",
                "project.key_unrecoverable",
                "credits.expiring",
                "credits.restored",
                "backup.made",
                "backup.failed",
                "backup.deleted",
                "drive.created",
                "drive.deleted",
                "drive.attached",
                "drive.detached",
                "drive.snapshot.created",
                "drive.snapshot.deleted",
                "database.created",
                "database.deleted",
                "database.branch_created",
                "database.branch_deleted",
                "database.restored",
                "key.created",
                "key.deleted",
                "instance.upgraded",
                "instance.upgrade_failed",
                "instance.reverted",
                "instance.restarted",
                "installation.created",
                "installation.ready",
                "installation.failed",
                "installation.updated",
                "installation.deleted"
              ]
            },
            "description": "The types it takes; empty: every type, those added later among them."
          },
          "enabled": {
            "type": "boolean",
            "description": "Whether events are sent to it: one that is not enabled is given none of the events recorded meanwhile."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "last_delivery": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/WebhookDelivery"
              },
              {
                "type": "null"
              }
            ],
            "description": "Null: nothing has been sent to it yet."
          }
        }
      },
      "WebhookUpdate": {
        "type": "object",
        "additionalProperties": false,
        "description": "What is given changes, and nothing else.",
        "properties": {
          "url": {
            "type": "string",
            "maxLength": 2048,
            "description": "`http://` or `https://`, with a host, at most 2048 characters. An address that resolves to none on the public internet (this machine, a private or link-local network, a reserved range) is not delivered to, unless the control plane is started so (`--webhook-allow-private true`, for development)."
          },
          "events": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "string",
              "enum": [
                "sandbox.created",
                "sandbox.deleted",
                "sandbox.restarted",
                "sandbox.slept",
                "sandbox.woke",
                "sandbox.moved",
                "sandbox.move_failed",
                "sandbox.hydrated",
                "sandbox.tokens_revoked",
                "sandbox.disk_limited",
                "browser.failed",
                "spend_limit.reached",
                "egress_overage_cap.reached",
                "subscription.started",
                "subscription.renewed",
                "subscription.changed",
                "subscription.charge_failed",
                "subscription.ended",
                "topup.collected",
                "topup.failed",
                "topup.refunded",
                "subscription.refunded",
                "withdrawal.received",
                "withdrawal.refunded",
                "balance.used_up",
                "balance.restored",
                "credits.expired",
                "storage_included.reached",
                "included.reached",
                "trust_limit.reached",
                "trust_level.changed",
                "limit_request.granted",
                "limit_request.refused",
                "auto_reload.no_card",
                "auto_reload.no_request",
                "account.in_debt",
                "account.debt_reminder",
                "account.debt_charge_failed",
                "account.debt_paid",
                "account.data_deleted",
                "account.held",
                "account.released",
                "tax.address_to_confirm",
                "tax.payment_held",
                "tax_id.updated",
                "tax_region.business_only",
                "domain.active",
                "domain.detached",
                "domain.lost",
                "domain.renewal_stopped",
                "domain.renewal_resumed",
                "app.deployed",
                "app.deploy_failed",
                "app.build_failed",
                "destination.failing",
                "destination.recovered",
                "database.archive_failing",
                "database.archive_recovered",
                "database.history_gap",
                "database.history_resumed",
                "project.deployed",
                "project.deploy_failed",
                "project.key_destroyed",
                "project.key_unrecoverable",
                "credits.expiring",
                "credits.restored",
                "backup.made",
                "backup.failed",
                "backup.deleted",
                "drive.created",
                "drive.deleted",
                "drive.attached",
                "drive.detached",
                "drive.snapshot.created",
                "drive.snapshot.deleted",
                "database.created",
                "database.deleted",
                "database.branch_created",
                "database.branch_deleted",
                "database.restored",
                "key.created",
                "key.deleted",
                "instance.upgraded",
                "instance.upgrade_failed",
                "instance.reverted",
                "instance.restarted",
                "installation.created",
                "installation.ready",
                "installation.failed",
                "installation.updated",
                "installation.deleted"
              ]
            },
            "description": "The types it takes from now on; null or empty: every type."
          },
          "enabled": {
            "type": "boolean"
          }
        }
      },
      "AuditList": {
        "type": "object",
        "additionalProperties": false,
        "description": "The project's audit log, the newest first.",
        "required": [
          "data",
          "next_cursor"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AuditEntry"
            }
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Where the next page begins; null on the last."
          }
        }
      },
      "AuditEntry": {
        "type": "object",
        "additionalProperties": false,
        "description": "A call that was no reading (every method but `GET` and `HEAD`: a command run and a file written among them), whatever came of it, a refused one too: who, what, when, from where and how it ended. Written before the answer went out. And every reading of an app's log by an operator of the platform (`log_read`): through the control plane (`GET /internal/apps/{app}/logs`), written before anything of it went out; and on a node with the node's key, written by the node before anything of it went out there, and taken into this log with the node's inventory seconds later, with the time of the reading by the node's clock and the node (`node_id`). The readings the control plane makes of the nodes for the project's keys, and those of the nodes' uploaders and exporters, are no operator's: none of them is here.",
        "required": [
          "id",
          "object",
          "created_at",
          "actor",
          "method",
          "path",
          "status",
          "request_id",
          "remote_address",
          "node_id",
          "log_read"
        ],
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^aud-[0-9a-hjkmnp-tv-z]{16}$"
          },
          "object": {
            "type": "string",
            "const": "audit_entry"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "actor": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "type",
              "id",
              "name"
            ],
            "description": "Who made the call: a key of the project (`key`, its id and its name), a sandbox token (`sandbox_token`, the sandbox's id, and no name), or a user signed in to the console (`session`: the user's id and its address), or an app the user connected with OAuth (`connection`: the connection's id, and the app's name with the user's address), or the platform itself (`platform`: what an account that owed past its date stored, deleted for its debt, whose id is the debt's, or what an account its owner closed stored, whose id is the account's), or an operator (`operator`, no id, and what was done: an account's trust level set, `POST /internal/accounts/{id}/trust`; an app's log read, through the control plane or on a node with its key).",
            "properties": {
              "type": {
                "type": "string",
                "x-extensible-enum": [
                  "key",
                  "sandbox_token",
                  "session",
                  "connection",
                  "platform",
                  "operator"
                ]
              },
              "id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "name": {
                "type": [
                  "string",
                  "null"
                ]
              }
            }
          },
          "method": {
            "type": "string"
          },
          "path": {
            "type": "string",
            "description": "The path as it came, without the query."
          },
          "status": {
            "type": "integer",
            "description": "The answer's status; 499 where the caller went before an answer."
          },
          "request_id": {
            "type": "string"
          },
          "remote_address": {
            "type": "string",
            "description": "The address that connected to the control plane: a load balancer's, where one stands in front of it."
          },
          "node_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "The node the call was passed to; null where none was. Of an operator's reading of an app's log on a node, that node."
          },
          "log_read": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": false,
            "description": "Of an operator's reading of an app's log: which app, the stretch of time it asked for (`since` null: from the first line kept; `until` null: up to the moment of the reading, `created_at`, and on while it followed), and whether it followed. Null on every other entry.",
            "required": [
              "app",
              "since",
              "until",
              "follow"
            ],
            "properties": {
              "app": {
                "type": "string",
                "description": "The app's id."
              },
              "since": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time"
              },
              "until": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time"
              },
              "follow": {
                "type": "boolean"
              }
            }
          }
        }
      },
      "TokenConnection": {
        "type": "object",
        "additionalProperties": false,
        "description": "The connection of an access token, its user, and what the token may do.",
        "required": [
          "object",
          "id",
          "client",
          "account",
          "projects",
          "scopes",
          "created_at",
          "granted_at",
          "last_used_at",
          "user",
          "token"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "connection"
          },
          "id": {
            "type": "string",
            "pattern": "^con-[0-9a-hjkmnp-tv-z]{16}$"
          },
          "client": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "name",
              "kind"
            ],
            "properties": {
              "id": {
                "type": "string",
                "description": "Its client id at the authorization server: Hydra's, or its metadata document's address."
              },
              "name": {
                "type": "string"
              },
              "kind": {
                "type": "string",
                "x-extensible-enum": [
                  "registered",
                  "document",
                  "operator"
                ],
                "description": "How it came to the authorization server: it registered itself (RFC 7591: Glac has not checked who made it), its id is its metadata document's address, or the operator made it."
              }
            }
          },
          "account": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "name"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": [
                  "string",
                  "null"
                ]
              }
            }
          },
          "projects": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "id",
                "name"
              ],
              "properties": {
                "id": {
                  "type": "string"
                },
                "name": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "description": "Null of one the account no longer has."
                }
              }
            }
          },
          "scopes": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "The scopes of the API the user granted."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "granted_at": {
            "type": "string",
            "format": "date-time",
            "description": "When it was last granted: a later consent to the same app grants in place of the one before."
          },
          "last_used_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "user": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "email"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "email": {
                "type": "string"
              }
            }
          },
          "token": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "scopes",
              "expires_at"
            ],
            "properties": {
              "scopes": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "The token's scopes that the connection still grants."
              },
              "expires_at": {
                "type": "string",
                "format": "date-time"
              }
            }
          }
        }
      },
      "Project": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id",
          "object",
          "name",
          "home_region",
          "private",
          "delete_after_idle_days",
          "created_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^prj-"
          },
          "object": {
            "type": "string",
            "enum": [
              "project"
            ]
          },
          "name": {
            "type": "string"
          },
          "home_region": {
            "type": [
              "string",
              "null"
            ],
            "x-extensible-enum": [
              "na",
              "eu",
              "ap"
            ],
            "description": "The first region of its first service with volumes; `null` while it has none."
          },
          "private": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "ranges"
            ],
            "properties": {
              "ranges": {
                "type": "array",
                "items": {
                  "type": "string",
                  "pattern": "^10\\.(6[4-9]|[7-9][0-9]|1[01][0-9]|12[0-7])(\\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){2}/26$"
                },
                "description": "The ranges of its private network, in the order they were cut; empty until it has an address."
              }
            }
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "delete_after_idle_days": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 1,
            "maximum": 3650,
            "description": "After how many days asleep a sandbox of the project is deleted by the control plane (`PATCH /v1/project`), with the event of its deletion saying `reason: idle`; looked at once an hour. Not a sandbox that is frozen, nor one with a `delete_after_idle_ms` of its own, nor an app's instance or a key's. `null`, which a project has until it says otherwise: none is deleted for this."
          }
        }
      },
      "ProjectUpdate": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "delete_after_idle_days": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 1,
            "maximum": 3650,
            "description": "After how many days asleep a sandbox of the project is deleted; `null`: never."
          }
        }
      },
      "VariableList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "data",
          "next_cursor"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Variable"
            }
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "Variable": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "name",
          "object",
          "value",
          "created_at",
          "updated_at"
        ],
        "properties": {
          "name": {
            "type": "string"
          },
          "object": {
            "type": "string",
            "enum": [
              "variable"
            ]
          },
          "value": {
            "type": "string"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "VariablePut": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "value"
        ],
        "properties": {
          "value": {
            "type": "string",
            "maxLength": 32768,
            "description": "Its value: text of at most 32 KiB, without NUL. Taken as it is written: a `${{` in it is no reference."
          }
        }
      },
      "VariablePutResult": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "name",
          "object",
          "value",
          "created_at",
          "updated_at",
          "deploy"
        ],
        "properties": {
          "name": {
            "type": "string"
          },
          "object": {
            "type": "string",
            "enum": [
              "variable"
            ]
          },
          "value": {
            "type": "string"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "deploy": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^dpl-[0-9a-hjkmnp-tv-z]{16}$",
            "description": "The deploy of the project the change made (`GET /v1/deploys/{id}`); null where no service's version is made of another value because of it."
          }
        }
      },
      "ComposeImport": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "model"
        ],
        "properties": {
          "model": {
            "type": "object",
            "description": "The Compose model, as `docker compose config --format json` writes it: its `services` (1 to 100), and its `volumes`, `networks`, `configs` (each with its `content`), `secrets` and the rest. At most 4 MiB."
          },
          "profiles": {
            "type": "array",
            "items": {
              "type": "string",
              "minLength": 1,
              "maxLength": 128
            },
            "description": "The profiles turned on: a service of profiles none of which is here is left out (`left_out`), as `docker compose --profile` leaves it out. Not given: none."
          },
          "dry_run": {
            "type": "boolean",
            "description": "`true`: the report alone; nothing is written, and the secrets need not be there. Not given: `false`."
          },
          "strict": {
            "type": "boolean",
            "description": "`true`: what is ignored is refused too. Not given: `false`."
          }
        }
      },
      "ProjectDeployCreate": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "services"
        ],
        "properties": {
          "services": {
            "type": "object",
            "minProperties": 1,
            "maxProperties": 100,
            "additionalProperties": {
              "$ref": "#/components/schemas/Deploy"
            },
            "description": "Services of the project, by name, each with what a deploy of it alone takes (`{}` for what it has)."
          }
        }
      },
      "ProjectDeploy": {
        "type": "object",
        "additionalProperties": false,
        "description": "A deploy of several of the project's services together (`POST /v1/deploys`, or a change of a shared variable). Kept for good, as an app's versions are.",
        "required": [
          "id",
          "object",
          "status",
          "cause",
          "services",
          "error",
          "replaced_by",
          "steps",
          "created_at",
          "started_at",
          "finished_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^dpl-[0-9a-hjkmnp-tv-z]{16}$"
          },
          "object": {
            "type": "string",
            "enum": [
              "deploy"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "queued",
              "deploying",
              "deployed",
              "failed",
              "replaced"
            ],
            "description": "`queued`: it waits for the project's deploy before it, `deploying`: it goes on, `deployed`: every one of its services is on its new version, `failed`: it ended at a service (`error`), `replaced`: a newer deploy took its place while it waited (`replaced_by`), and deploys what it was for."
          },
          "cause": {
            "type": "string",
            "enum": [
              "api",
              "variables",
              "compose"
            ],
            "description": "What made it: `POST /v1/deploys`, a change of a shared variable, or an import of a Compose file (`POST /v1/compose`)."
          },
          "services": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ProjectDeployService"
            },
            "description": "Its services: once it is taken up, those it deploys in their order and then those that needed nothing; before, the services it is for."
          },
          "error": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": false,
            "required": [
              "code",
              "message",
              "service"
            ],
            "properties": {
              "code": {
                "type": "string"
              },
              "message": {
                "type": "string"
              },
              "service": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Where it failed; null where it failed before any service's turn."
              }
            },
            "description": "Why it failed; null otherwise."
          },
          "replaced_by": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^dpl-[0-9a-hjkmnp-tv-z]{16}$"
          },
          "steps": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "plan_ms",
              "stateful_ms",
              "jobs_ms",
              "templates_ms",
              "warm_ms",
              "switch_ms"
            ],
            "properties": {
              "plan_ms": {
                "type": [
                  "integer",
                  "null"
                ],
                "minimum": 0,
                "description": "Working out its order and making its versions."
              },
              "stateful_ms": {
                "type": [
                  "integer",
                  "null"
                ],
                "minimum": 0,
                "description": "Its stateful services: their templates, and their instances replaced, one after the other."
              },
              "jobs_ms": {
                "type": [
                  "integer",
                  "null"
                ],
                "minimum": 0,
                "description": "Its jobs, built and run to their ends."
              },
              "templates_ms": {
                "type": [
                  "integer",
                  "null"
                ],
                "minimum": 0,
                "description": "The templates of the rest, from the first build's beginning to the last one's end."
              },
              "warm_ms": {
                "type": [
                  "integer",
                  "null"
                ],
                "minimum": 0,
                "description": "The nodes told of those versions, each with an instance of it up."
              },
              "switch_ms": {
                "type": [
                  "integer",
                  "null"
                ],
                "minimum": 0,
                "description": "Their traffic switched: the transaction, the edges, the project's network on its nodes, and the versions they left taken off the nodes."
              }
            },
            "description": "How long each step took; null for a step it did not come to, or had nothing for."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "started_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "finished_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "ProjectDeployService": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "service",
          "app_id",
          "kind",
          "stateful",
          "status",
          "version_id",
          "version",
          "previous_version_id",
          "waits_for",
          "error",
          "started_at",
          "finished_at",
          "exit_code"
        ],
        "properties": {
          "service": {
            "type": "string",
            "description": "Its name when the deploy was made or taken up."
          },
          "app_id": {
            "type": "string",
            "pattern": "^app-[0-9a-hjkmnp-tv-z]{16}$"
          },
          "kind": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "service",
              "job",
              null
            ],
            "description": "Null until the deploy is taken up."
          },
          "stateful": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "It has a volume of its own. Null until the deploy is taken up."
          },
          "status": {
            "type": "string",
            "enum": [
              "waiting",
              "building",
              "starting",
              "running",
              "ready",
              "deployed",
              "completed",
              "unchanged",
              "failed",
              "not_deployed"
            ],
            "description": "`waiting` for its turn; `building` its template; `starting` (a stateful service's instance) or `running` (a job's); `ready`: built, to be switched with the rest; `deployed`: on its new version; `completed`: a job that ended with 0; `unchanged`: nothing of it was to change; `failed`: the deploy ended at it (`error`); `not_deployed`: the deploy ended before it was switched."
          },
          "version_id": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^ver-[0-9a-hjkmnp-tv-z]{16}$",
            "description": "The version it deploys: one the deploy made, or a job's current one where nothing of it changed. Null for one that is unchanged."
          },
          "version": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 1
          },
          "previous_version_id": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^ver-[0-9a-hjkmnp-tv-z]{16}$",
            "description": "Its current version when the deploy was taken up."
          },
          "waits_for": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "The services of the deploy it waited for, by name."
          },
          "error": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": false,
            "required": [
              "code",
              "message"
            ],
            "properties": {
              "code": {
                "type": "string"
              },
              "message": {
                "type": "string"
              }
            }
          },
          "started_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "finished_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "exit_code": {
            "type": [
              "integer",
              "null"
            ],
            "description": "How a job's run ended."
          }
        }
      },
      "ProjectDeployList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "data",
          "next_cursor"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ProjectDeploy"
            }
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "VolumeBackupCreate": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "expires_in_days": {
            "type": "integer",
            "minimum": 1,
            "maximum": 365,
            "description": "In how many days it goes; the volume's rule's `expires_in_days` unless given (30 unless the volume says otherwise)."
          }
        }
      },
      "VolumeBackup": {
        "type": "object",
        "additionalProperties": false,
        "description": "A copy of one of the project's volumes at one instant, which the object store holds once it is `ready`, sealed (control/README.md, 卷的备份). A volume is made from it (`POST /v1/volumes` with `from_backup`) on any node of its region that makes volumes from backups. It outlives its volume, and is the project's until it expires or is deleted.",
        "required": [
          "id",
          "object",
          "volume_id",
          "volume_name",
          "status",
          "reason",
          "consistency",
          "frozen_ms",
          "size_bytes",
          "volume_size_bytes",
          "stored_bytes",
          "own_bytes",
          "shared_bytes",
          "shared",
          "region",
          "created_at",
          "ready_at",
          "expires_at",
          "failure"
        ],
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^bak-[0-9a-hjkmnp-tv-z]{16,20}$"
          },
          "object": {
            "type": "string",
            "const": "backup"
          },
          "volume_id": {
            "type": "string",
            "pattern": "^vol-[0-9a-hjkmnp-tv-z]{16}$",
            "description": "The volume it was taken of, which may be gone."
          },
          "volume_name": {
            "type": [
              "string",
              "null"
            ],
            "description": "That volume's name when it was taken."
          },
          "status": {
            "type": "string",
            "enum": [
              "making",
              "ready",
              "failed"
            ],
            "description": "`making` from the moment its node has the copy (before the call that asked for it is answered) until the node's uploader has it in the object store; then `ready`, or `failed` (`failure` says why), from which nothing is made."
          },
          "reason": {
            "type": "string",
            "enum": [
              "manual",
              "daily",
              "before_version"
            ],
            "description": "Why it was taken: `manual`, by a call (`POST /v1/volumes/{id}/backups`); `daily`, by its volume's rule; `before_version`, before a version of a stateful service replaced another (control/README.md, 有状态的服务)."
          },
          "consistency": {
            "oneOf": [
              {
                "type": "string",
                "enum": [
                  "frozen",
                  "crash"
                ]
              },
              {
                "type": "null"
              }
            ],
            "description": "`frozen`: the volume's file system is at rest in the copy (it was frozen for the instant of the copy, or no running machine had it and it was unmounted in order), and mounts without replaying its journal; `crash`: the disk at one instant, as after a power cut (a sleeping sandbox's volume, or one whose guest did not freeze), which the file system comes back from by its journal and a database by its log."
          },
          "frozen_ms": {
            "type": [
              "number",
              "null"
            ],
            "minimum": 0,
            "description": "For how long the writers to the volume's file system waited for the copy, in milliseconds; null where it was not frozen."
          },
          "size_bytes": {
            "type": "integer",
            "format": "int64",
            "minimum": 0,
            "description": "What the volume held when it was taken: the bytes of its file on its node."
          },
          "volume_size_bytes": {
            "type": "integer",
            "format": "int64",
            "minimum": 0,
            "description": "The most the volume held: what a volume made from it holds."
          },
          "stored_bytes": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int64",
            "minimum": 0,
            "description": "What it is billed for, once it is `ready`: the bytes the object store holds (compressed and sealed) that it needs and no earlier backup of its volume that is there needs. A backup is incremental: it holds what changed since the backup of its volume before it, and points at that one's blocks for the rest; a block several backups need is held once and counted once, on the earliest of them (when that one is deleted, on the next). The account's backups add up to what the store holds of them."
          },
          "own_bytes": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int64",
            "minimum": 0,
            "description": "What deleting it would save: the bytes the object store holds that it needs and no other backup of its volume that is there needs. Null while it is being made, and for one that failed."
          },
          "shared_bytes": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int64",
            "minimum": 0,
            "description": "The bytes it needs that other backups of its volume need too, wherever they are counted (`stored_bytes` of the earliest of them). Null while it is being made, and for one that failed."
          },
          "shared": {
            "type": "boolean",
            "description": "Whether its volume was a shared one: a volume made from it is."
          },
          "region": {
            "type": [
              "string",
              "null"
            ],
            "description": "The region of the node that took it, where a volume is made from it; null for a node of no region."
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "The instant of the copy, by the control plane's clock."
          },
          "ready_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the control plane saw it in the object store (`ready`), by its clock: what it keeps is billed from then (its `stored_bytes` times the hours, on the meter of state, at the price of state, which what the plan includes of storage covers first) to its delete, or its expiry where that came first. Null while it is being made, and for one that failed, which costs nothing."
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "description": "When it goes: a backup taken by a call, after the days the call gave or its volume's rule's `expires_in_days` (30 unless the volume says otherwise); a daily one, `keep` days after it was taken."
          },
          "failure": {
            "type": [
              "string",
              "null"
            ],
            "description": "Why it failed; null otherwise."
          }
        }
      },
      "VolumeBackupList": {
        "type": "object",
        "additionalProperties": false,
        "description": "One page of backups, the newest first.",
        "required": [
          "data",
          "next_cursor"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/VolumeBackup"
            }
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Where the next page begins; null on the last."
          }
        }
      },
      "SshKeyCreate": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "public_key"
        ],
        "properties": {
          "public_key": {
            "type": "string",
            "description": "One line as a `.pub` file holds it: `<type> <base64> [comment]`. Taken: `ssh-ed25519`, `ssh-rsa` of 2048 bits or more, `ecdsa-sha2-nistp256`, `ecdsa-sha2-nistp384`."
          },
          "name": {
            "type": "string",
            "description": "1 to 63 lower-case letters, digits and hyphens, without a hyphen first or last, and not beginning as an id does."
          },
          "sandbox_id": {
            "type": "string",
            "description": "A sandbox's id or name: the key opens that sandbox and no other. Left out: every sandbox of the project."
          },
          "expires_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "A time in the future from which the key opens nothing; null or left out: it does not run out."
          }
        }
      },
      "SshKey": {
        "type": "object",
        "additionalProperties": false,
        "description": "A public key that opens sandboxes of the project through the SSH gateway: the node's `SshKey` (`sandboxd/openapi/openapi.json`), kept by the control plane for every node.",
        "required": [
          "id",
          "object",
          "name",
          "public_key",
          "type",
          "fingerprint",
          "comment",
          "sandbox_id",
          "created_at",
          "expires_at",
          "last_used_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^sshkey-[0-9a-hjkmnp-tv-z]{16}$"
          },
          "object": {
            "type": "string",
            "const": "ssh_key"
          },
          "name": {
            "type": [
              "string",
              "null"
            ],
            "description": "1 to 63 lower-case letters, digits and hyphens; null: none was given."
          },
          "public_key": {
            "type": "string",
            "description": "`<type> <base64>`: the line it was registered with, without what stood after the key."
          },
          "type": {
            "type": "string",
            "x-extensible-enum": [
              "ssh-ed25519",
              "ssh-rsa",
              "ecdsa-sha2-nistp256",
              "ecdsa-sha2-nistp384"
            ]
          },
          "fingerprint": {
            "type": "string",
            "pattern": "^SHA256:[A-Za-z0-9+/]{43}$",
            "description": "As `ssh-keygen -l` prints it."
          },
          "comment": {
            "type": "string",
            "description": "What stood after the key in the line: empty where nothing did."
          },
          "sandbox_id": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^sbx-[0-9a-hjkmnp-tv-z]{16}$",
            "description": "The one sandbox the key opens; null: every sandbox of the project."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "expires_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "From this time on the key opens nothing; null: it does not run out."
          },
          "last_used_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When a session last began by the key, to the minute; null: none has."
          }
        }
      },
      "SshKeyList": {
        "type": "object",
        "additionalProperties": false,
        "description": "The project's SSH keys, the newest first.",
        "required": [
          "data",
          "next_cursor"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SshKey"
            }
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Where the next page begins; null on the last."
          }
        }
      },
      "BrowserCreate": {
        "type": "object",
        "additionalProperties": false,
        "description": "A session to make. Every field may be left out.",
        "properties": {
          "kind": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "chromium-headless",
              "chromium",
              "lightpanda",
              null
            ],
            "description": "`chromium-headless` (the default), `chromium` or `lightpanda`."
          },
          "name": {
            "type": [
              "string",
              "null"
            ],
            "description": "Its sandbox's name: the project's for one sandbox at a time. A create with a name the project's session has answers that session."
          },
          "region": {
            "type": [
              "string",
              "null"
            ],
            "description": "`na`, `eu` or `ap`; the control plane's own where none is given."
          },
          "profile": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": false,
            "required": [
              "name"
            ],
            "description": "A profile to load: its newest version, or `version`, put in place of the browser's own before the session is answered. `save: true`: the session saves it when it is deleted (and a profile that has no version yet is made by that save).",
            "properties": {
              "name": {
                "type": "string",
                "pattern": "^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$"
              },
              "save": {
                "type": [
                  "boolean",
                  "null"
                ],
                "description": "False when not given."
              },
              "version": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "One of its versions; the newest when not given."
              }
            }
          },
          "proxy": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "url": {
                "type": "string",
                "maxLength": 2048,
                "description": "`http://user:password@host:port` (the port 80 where it names none; a user name and a password written %XX where they hold what a URL does not take, as `@` or `:`): an HTTP proxy that takes `CONNECT`, at a host name or an address on the internet. Only `http://`: a proxy reached over TLS, and SOCKS, are not served."
              }
            },
            "description": "Your own proxy (BR11), on a paid plan: `{\"url\": ...}`; null or not given: none. The session's TCP connections to ports 80 and 443 go out through it, by its node's relay: in the layer its egress rules are enforced in, which reads where each goes (the TLS handshake's server name, the HTTP request's Host) and asks the proxy for a tunnel there (`CONNECT`, the credentials in Basic). The browser is not told of it, and nothing in the guest holds the credentials; they are the node's to keep, and no answer has them. UDP to port 443 (QUIC) is refused, so the browser speaks HTTP over TCP; connections to other ports go as they went. What goes through it is counted both ways (`usage.proxy_bytes`) and is ordinary egress. A connection the proxy takes no tunnel for is told so: an HTTP request 502 `proxy_failed`, a TLS handshake an alert. 403 `proxy_not_on_plan` on the free plan; 400 `invalid_proxy` for what is no proxy of yours (`param` `proxy.url`; the message never says the URL); 400 `unknown_field` for a field of it other than `url`."
          },
          "sleep_after_seconds": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 1,
            "maximum": 3600,
            "description": "How long it is asleep after its last connection closed (or its last request): 10 when not given."
          },
          "connection_idle_seconds": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 60,
            "maximum": 86400,
            "description": "How long a connection to its browser (or its screen) may say nothing either way before its node closes it: 600 when not given (null in a change: back to 600)."
          },
          "delete_after_idle_seconds": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 60,
            "maximum": 31622400,
            "description": "How long it sleeps before it is deleted; null or not given: never for that."
          },
          "metadata": {
            "type": [
              "object",
              "null"
            ],
            "maxProperties": 64,
            "propertyNames": {
              "minLength": 1,
              "maxLength": 64
            },
            "additionalProperties": {
              "type": "string",
              "maxLength": 1024
            },
            "description": "Free key-value pairs for the caller's own use; not `product`, which is the platform's."
          }
        }
      },
      "Browser": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "id",
          "name",
          "kind",
          "status",
          "error",
          "region",
          "cdp_url",
          "ws_url",
          "live_url",
          "profile",
          "proxy",
          "connections",
          "sleep_after_seconds",
          "connection_idle_seconds",
          "delete_after_idle_seconds",
          "metadata",
          "created_at",
          "usage"
        ],
        "description": "A Browsers session: a sandbox of the node's browser image, 1 vCPU and 2 GiB, which the control plane marked as a session when `POST /v1/browsers` made it.",
        "properties": {
          "object": {
            "type": "string",
            "const": "browser"
          },
          "id": {
            "type": "string",
            "pattern": "^sbx-[0-9a-hjkmnp-tv-z]{16}$",
            "description": "Its sandbox's id."
          },
          "name": {
            "type": [
              "string",
              "null"
            ]
          },
          "kind": {
            "type": "string",
            "enum": [
              "chromium-headless",
              "chromium",
              "lightpanda"
            ],
            "description": "`chromium-headless`: Chromium's headless shell; `chromium`: the full browser on a screen, watched at `live_url`; `lightpanda`: Lightpanda, which runs a page's scripts and keeps its DOM and has no layout engine (its screenshots and PDFs hold the page's text alone; no `live_url`, no profile)."
          },
          "status": {
            "type": "string",
            "enum": [
              "awake",
              "asleep",
              "unavailable",
              "failed"
            ],
            "description": "`awake` while its sandbox runs, `asleep` while it sleeps (or is held asleep), `unavailable` where its node cannot be reached, `failed` where its sandbox failed after its create was answered (its browser stopped and did not come back, say): `error` says why. One that fails before it runs is no session: its create is refused (503 `session_failed`)."
          },
          "error": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": false,
            "required": [
              "code",
              "message"
            ],
            "description": "Why it failed, as its node says it (its sandbox's `failure_code` and `failure`); null unless `status` is `failed`.",
            "properties": {
              "code": {
                "type": "string"
              },
              "message": {
                "type": "string"
              }
            }
          },
          "region": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "na",
              "eu",
              "ap",
              null
            ],
            "description": "The region of the node that holds it; null where that node is in none."
          },
          "cdp_url": {
            "type": [
              "string",
              "null"
            ],
            "description": "Where CDP clients connect: the signed address of its port 9222 (`https://9222-<id>-<signature>.<preview domain>`), the credential in its host name. Null for a key without the `url` scope, or where its node has no preview domain."
          },
          "ws_url": {
            "type": [
              "string",
              "null"
            ],
            "description": "The browser's own WebSocket address (`wss://9222-<id>-<signature>.<preview domain>/devtools/browser/<browser id>`), for the clients that take nothing else (Puppeteer's `browserWSEndpoint`, chromedp's remote allocator): the path of the browser its node recorded when the session's template was built (the sandbox's `devtools`) at `cdp_url`, whose credential it carries. Null where `cdp_url` is, or where the node recorded no browser. `cdp_url` serves every CDP client that asks `/json/version`."
          },
          "live_url": {
            "type": [
              "string",
              "null"
            ],
            "description": "Where its page is watched and driven, live, at a signed address (rotated with `cdp_url`): a headless session's `/live` on its CDP port (the image's page drawing the tab's screencast, the viewer's mouse and keyboard passed on), a headful one's screen (noVNC on port 6080). The read-only address is `GET /v1/browsers/{id}/live?interactive=false`. Null without the `url` scope, or where its node has no preview domain, and always for a `lightpanda` session, which has nothing to stream."
          },
          "profile": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": false,
            "required": [
              "name",
              "save",
              "version"
            ],
            "description": "The profile it was made with, or saved last: null for none.",
            "properties": {
              "name": {
                "type": "string"
              },
              "save": {
                "type": "boolean",
                "description": "Whether it saves the profile when it is deleted."
              },
              "version": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "The version it holds: the one it loaded, or saved last; null for a profile it has not saved yet."
              }
            }
          },
          "proxy": {
            "oneOf": [
              {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "url",
                  "username"
                ],
                "properties": {
                  "url": {
                    "type": "string",
                    "description": "`http://host:port`: the proxy's address, without the credentials."
                  },
                  "username": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "description": "The user name it is asked with; null for none."
                  }
                }
              },
              {
                "type": "null"
              }
            ],
            "description": "Its own proxy (BR11), as its node shows it: its address and the user name it is asked with, never its password. Null for none."
          },
          "connections": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "description": "The connections to its browser (its live page's among them), to a headful one's screen and to the read-only view, open now through its preview addresses: it is awake while there is one (its sandbox's `awake_connections`). Null from a node that does not count them."
          },
          "sleep_after_seconds": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "maximum": 3600,
            "description": "How long after its last connection closed (and its last request) it falls asleep."
          },
          "connection_idle_seconds": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 1,
            "description": "How long a connection to it may say nothing either way before its node closes it; null from a node that does not close them."
          },
          "delete_after_idle_seconds": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 1,
            "description": "How long it sleeps before it is deleted; null: it is never deleted for that."
          },
          "metadata": {
            "type": "object",
            "maxProperties": 64,
            "propertyNames": {
              "minLength": 1,
              "maxLength": 64
            },
            "additionalProperties": {
              "type": "string",
              "maxLength": 1024
            },
            "description": "Its sandbox's metadata: the caller's own. Never what it is sold as, which is the control plane's record."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "usage": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "awake_seconds",
              "proxy_bytes",
              "proxy_sent_bytes",
              "proxy_received_bytes"
            ],
            "properties": {
              "awake_seconds": {
                "type": "number",
                "minimum": 0,
                "description": "How long it has been awake since it was made, in seconds to the millisecond: what the books have of it on the meter of the sessions, as its node counts it, which is what it is billed by."
              },
              "proxy_bytes": {
                "type": "integer",
                "minimum": 0,
                "description": "What went through its own proxy since it was made, both ways: `proxy_sent_bytes` and `proxy_received_bytes` together, as its node's relay counted them, byte by byte. Ordinary egress: what left the node for the proxy is among its sandbox's `egress_bytes`."
              },
              "proxy_sent_bytes": {
                "type": "integer",
                "minimum": 0,
                "description": "Of it, what the session sent through its proxy's tunnels."
              },
              "proxy_received_bytes": {
                "type": "integer",
                "minimum": 0,
                "description": "Of it, what came back to the session through them."
              }
            }
          }
        }
      },
      "BrowserList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "data",
          "next_cursor"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Browser"
            }
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "BrowserUpdate": {
        "type": "object",
        "additionalProperties": false,
        "description": "What to change of a session. Every field may be left out.",
        "properties": {
          "sleep_after_seconds": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 1,
            "maximum": 3600,
            "description": "Null puts it back to 10."
          },
          "connection_idle_seconds": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 60,
            "maximum": 86400,
            "description": "How long a connection to its browser (or its screen) may say nothing either way before its node closes it: 600 when not given (null in a change: back to 600)."
          },
          "delete_after_idle_seconds": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 60,
            "maximum": 31622400,
            "description": "Null: never deleted for sleeping."
          },
          "metadata": {
            "type": [
              "object",
              "null"
            ],
            "maxProperties": 64,
            "propertyNames": {
              "minLength": 1,
              "maxLength": 64
            },
            "additionalProperties": {
              "type": "string",
              "maxLength": 1024
            },
            "description": "In place of what it has (null: none); not `product`."
          },
          "proxy": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "url": {
                "type": "string",
                "maxLength": 2048,
                "description": "As at its create."
              }
            },
            "description": "Another proxy of yours, or none (null), as at its create (BR11): the browser goes on as it is (it is not started again), its connections through the proxy before end, and its next go through this one. 403 `proxy_not_on_plan` on the free plan (null is taken)."
          }
        }
      },
      "BrowserLive": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "id",
          "url",
          "interactive"
        ],
        "description": "Where a Browsers session is watched live.",
        "properties": {
          "object": {
            "type": "string",
            "const": "browser_live"
          },
          "id": {
            "type": "string",
            "description": "The session's id."
          },
          "url": {
            "type": "string",
            "description": "The signed address: open it in a browser, or put it in a frame of a page of one's own."
          },
          "interactive": {
            "type": "boolean",
            "description": "Whether the viewer's mouse and keyboard reach the page."
          }
        }
      },
      "BrowserProfileSave": {
        "type": "object",
        "additionalProperties": false,
        "description": "What to save the session's profile as.",
        "properties": {
          "name": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$",
            "description": "The profile; the one the session was made with when not given."
          }
        }
      },
      "BrowserProfile": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "name",
          "version",
          "bytes",
          "saved_at",
          "created_at",
          "versions"
        ],
        "description": "A browser profile of the project's: what says who a browser is to the sites it visited, kept by the platform for the project behind its object gateway.",
        "properties": {
          "object": {
            "type": "string",
            "const": "browser_profile"
          },
          "name": {
            "type": "string"
          },
          "version": {
            "type": [
              "string",
              "null"
            ],
            "description": "The newest version: the one a session made with the profile loads."
          },
          "bytes": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "description": "The newest version's size."
          },
          "saved_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the newest version was saved."
          },
          "created_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When its first version was saved."
          },
          "versions": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "version",
                "bytes",
                "sha256",
                "saved_at",
                "session_id"
              ],
              "properties": {
                "version": {
                  "type": "string"
                },
                "bytes": {
                  "type": "integer",
                  "minimum": 0,
                  "description": "The archive's size, as it is kept (tar, gzip)."
                },
                "sha256": {
                  "type": "string",
                  "description": "Of the archive."
                },
                "saved_at": {
                  "type": "string",
                  "format": "date-time"
                },
                "session_id": {
                  "type": "string",
                  "description": "The session that saved it."
                }
              }
            },
            "description": "Every version it has, the newest first."
          }
        }
      },
      "BrowserProfileList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "data",
          "next_cursor"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/BrowserProfile"
            }
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Where the next page begins; null on the last."
          }
        }
      },
      "CatalogueList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "data",
          "next_cursor"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CatalogueApp"
            }
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Pass it as `cursor` to get the next page. Null on the last page."
          }
        }
      },
      "CatalogueApp": {
        "type": "object",
        "additionalProperties": false,
        "description": "An app of the catalogue of Managed Applications, as its version shows it.",
        "required": [
          "object",
          "app",
          "version",
          "title",
          "summary",
          "description",
          "publisher",
          "verified",
          "licence",
          "source",
          "docs",
          "versions",
          "parameters",
          "services",
          "buckets",
          "requires",
          "outputs",
          "api",
          "calculator",
          "published_at"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "catalogue_app"
          },
          "app": {
            "type": "string",
            "description": "Its name in the catalogue."
          },
          "version": {
            "type": "string",
            "description": "The version shown: the newest that is not withdrawn, or the one asked for."
          },
          "title": {
            "type": "string"
          },
          "summary": {
            "type": [
              "string",
              "null"
            ]
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "publisher": {
            "type": "string",
            "description": "The account that publishes it: `glac` for what the platform packages."
          },
          "verified": {
            "type": "boolean",
            "description": "Whether the platform publishes it."
          },
          "licence": {
            "type": "string",
            "description": "The SPDX expression of what its images run."
          },
          "source": {
            "type": [
              "string",
              "null"
            ],
            "description": "Where its source is."
          },
          "docs": {
            "type": [
              "string",
              "null"
            ],
            "description": "Where its own documentation is."
          },
          "versions": {
            "type": "array",
            "description": "Its versions that are not withdrawn, the newest first.",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "version",
                "published_at"
              ],
              "properties": {
                "version": {
                  "type": "string"
                },
                "published_at": {
                  "type": "string",
                  "format": "date-time"
                }
              }
            }
          },
          "parameters": {
            "type": "object",
            "additionalProperties": true,
            "description": "The JSON Schema of what an installation sets."
          },
          "services": {
            "type": "array",
            "description": "What one installation is, as Cube services.",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "name",
                "kind",
                "volumes"
              ],
              "properties": {
                "name": {
                  "type": "string"
                },
                "kind": {
                  "type": "string",
                  "enum": [
                    "serverless",
                    "stateful",
                    "keyed"
                  ]
                },
                "volumes": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                }
              }
            }
          },
          "buckets": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "The buckets of its own an installation has, billed as volume data."
          },
          "requires": {
            "type": "array",
            "description": "The other apps of the catalogue an installation needs.",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "name",
                "app",
                "version"
              ],
              "properties": {
                "name": {
                  "type": "string"
                },
                "app": {
                  "type": "string"
                },
                "version": {
                  "type": [
                    "string",
                    "null"
                  ]
                }
              }
            }
          },
          "outputs": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "What an installation shows."
          },
          "api": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": false,
            "required": [
              "base"
            ],
            "properties": {
              "base": {
                "type": "string"
              }
            },
            "description": "Where its own API is (`/v1/managed/<app>`), where it has one."
          },
          "calculator": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": false,
            "required": [
              "inputs",
              "meters",
              "month"
            ],
            "description": "Its calculator: inputs with their defaults, and the Cube meters a month makes of them.",
            "properties": {
              "inputs": {
                "type": "object",
                "additionalProperties": {
                  "type": "number"
                }
              },
              "meters": {
                "type": "object",
                "additionalProperties": {
                  "type": "string"
                }
              },
              "month": {
                "type": "object",
                "description": "What the meters come to in a month at the inputs' defaults: each meter's amount, in its unit (vCPU-hours, GiB-hours, requests, GB-months, GB).",
                "additionalProperties": {
                  "type": "number",
                  "minimum": 0
                }
              }
            }
          },
          "published_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "definition": {
            "type": "object",
            "additionalProperties": true,
            "description": "Of one app read by itself: its definition, as it was published."
          }
        }
      },
      "InstallationCreate": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "app"
        ],
        "properties": {
          "app": {
            "type": "string",
            "description": "The app of the catalogue to install."
          },
          "name": {
            "type": "string",
            "description": "1 to 24 lower-case letters, digits and hyphens, beginning with a letter; its apps are `<name>-<service>`. Left out: the app's name."
          },
          "region": {
            "type": "string",
            "enum": [
              "na",
              "eu",
              "ap"
            ],
            "description": "Where its services run and its Volumes and buckets are. Left out: the control plane's own region."
          },
          "version": {
            "type": "string",
            "description": "A version of the app that is not withdrawn. Left out: its newest."
          },
          "parameters": {
            "type": "object",
            "additionalProperties": true,
            "description": "What the app's schema lets an installation set; each left out at its default."
          },
          "requires": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "Each requirement of the app bound to one of the project's installations of the app it requires (by its id or name), in the place of one made for it."
          }
        }
      },
      "Installation": {
        "type": "object",
        "additionalProperties": false,
        "description": "An installation of an app of the catalogue in the project.",
        "required": [
          "id",
          "object",
          "name",
          "app",
          "version",
          "region",
          "status",
          "failure",
          "parameters",
          "outputs",
          "secret_outputs",
          "apps",
          "volumes",
          "buckets",
          "requires",
          "required_by",
          "api",
          "created_at",
          "updated_at",
          "deleted_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^ins-[0-9a-hjkmnp-tv-z]{16}$"
          },
          "object": {
            "type": "string",
            "const": "installation"
          },
          "name": {
            "type": "string"
          },
          "app": {
            "type": "string",
            "description": "The app of the catalogue."
          },
          "version": {
            "type": "string",
            "description": "The version of its definition it is of."
          },
          "region": {
            "type": "string",
            "enum": [
              "na",
              "eu",
              "ap"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "installing",
              "ready",
              "updating",
              "failed",
              "deleting",
              "deleted"
            ],
            "description": "`installing` until its apps' first versions are ready and its app's controller (where it has one) has taken it, `updating` after an update until they are again, `failed` where one failed (`failure` says why)."
          },
          "failure": {
            "type": [
              "string",
              "null"
            ]
          },
          "parameters": {
            "type": "object",
            "additionalProperties": true,
            "description": "As it was installed or last updated, every default filled in; those its app's schema marks secret null where the key may not manage the project."
          },
          "outputs": {
            "type": "object",
            "additionalProperties": {
              "type": [
                "string",
                "number",
                "boolean",
                "null"
              ]
            },
            "description": "What it shows (an address, a connection string); a secret one (`secret_outputs`) null where the key may not manage the project."
          },
          "secret_outputs": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "The outputs made of a secret."
          },
          "apps": {
            "type": "array",
            "description": "Its services' apps, each by its name in the definition.",
            "items": {
              "$ref": "#/components/schemas/InstallationPart"
            }
          },
          "volumes": {
            "type": "array",
            "description": "Its Drives, each by `<service>/<volume>`.",
            "items": {
              "$ref": "#/components/schemas/InstallationPart"
            }
          },
          "buckets": {
            "type": "array",
            "description": "Its buckets, each with what it held when it was last measured (every five minutes), which is billed as volume data under its id.",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "name",
                "id",
                "objects",
                "bytes",
                "measured_at"
              ],
              "properties": {
                "name": {
                  "type": "string"
                },
                "id": {
                  "type": "string",
                  "pattern": "^ibk-[0-9a-hjkmnp-tv-z]{16}$"
                },
                "objects": {
                  "type": "integer"
                },
                "bytes": {
                  "type": "integer",
                  "format": "int64"
                },
                "measured_at": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "format": "date-time"
                }
              }
            }
          },
          "requires": {
            "type": "array",
            "description": "The installations it requires: made for it, or bound (`bound`).",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "name",
                "id"
              ],
              "properties": {
                "name": {
                  "type": "string"
                },
                "id": {
                  "type": "string"
                },
                "bound": {
                  "type": "boolean",
                  "const": true
                }
              }
            }
          },
          "required_by": {
            "type": [
              "string",
              "null"
            ],
            "description": "The installation it was made for, which requires it."
          },
          "api": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": false,
            "required": [
              "base"
            ],
            "properties": {
              "base": {
                "type": "string"
              }
            }
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "deleted_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "InstallationPart": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "name",
          "id"
        ],
        "properties": {
          "name": {
            "type": "string"
          },
          "id": {
            "type": "string"
          }
        }
      },
      "InstallationList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "data",
          "next_cursor"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Installation"
            }
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "InstallationUpdate": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "version": {
            "type": "string",
            "description": "Another version of its app (not withdrawn)."
          },
          "parameters": {
            "type": "object",
            "additionalProperties": true,
            "description": "A JSON Merge Patch of its parameters: null takes one back to its default."
          }
        }
      },
      "RivetHealth": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "installation_id",
          "engine"
        ],
        "description": "An installation of Rivet's engine, as it answered.",
        "properties": {
          "object": {
            "type": "string",
            "const": "rivet_health"
          },
          "installation_id": {
            "type": "string",
            "pattern": "^ins-[0-9a-hjkmnp-tv-z]{16}$"
          },
          "engine": {
            "type": "object",
            "additionalProperties": true,
            "description": "The engine's own answer to `GET /health`, as it gave it: Rivet 3.0's has `status` (`ok`), `runtime`, `version` and `architecture`."
          }
        }
      },
      "DatabaseCreate": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "name"
        ],
        "description": "A database to make. Every field but `name` may be left out.",
        "properties": {
          "name": {
            "type": "string",
            "description": "Lower-case letters, digits and hyphens: its name in the project and on its network, which its app has too."
          },
          "engine": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "postgres",
              null
            ]
          },
          "version": {
            "type": [
              "string",
              "integer",
              "null"
            ],
            "description": "16, 17 or 18 (the default)."
          },
          "region": {
            "type": [
              "string",
              "null"
            ],
            "description": "`na`, `eu` or `ap`; the project's home where none is given."
          },
          "database_name": {
            "type": [
              "string",
              "null"
            ],
            "description": "The database in the cluster: a lower-case identifier, 57 characters at the most, not `postgres`, `template0`, `template1` nor `pg_…`. Default `glacdb`; its owner is `<database_name>_owner`."
          },
          "size": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": false,
            "properties": {
              "vcpus": {
                "type": "integer"
              },
              "memory_bytes": {
                "type": "integer"
              }
            },
            "description": "Upper bounds, held to the plan's: default 1 vCPU and 1 GiB."
          },
          "storage": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": false,
            "properties": {
              "size_bytes": {
                "type": [
                  "integer",
                  "null"
                ]
              },
              "durability": {
                "type": [
                  "string",
                  "null"
                ],
                "enum": [
                  "strict",
                  "standard",
                  null
                ]
              }
            },
            "description": "Its Drive: `size_bytes` (default: a Drive's), `durability` (`strict`, the default: a commit returns once the object store has it; `standard`: once the node has it)."
          },
          "sleep_after_seconds": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 1,
            "maximum": 3600,
            "description": "After how long with nothing asked of it its instance is paused, in seconds: from 1 (the default) to 3600 (an hour) on a paid plan. On Free 1, fixed: a Free plan's database is paused a second after its last query and sleeps five minutes later, as Neon's Free scales to zero; another value is refused 400 `invalid_field`."
          },
          "suspend_after_seconds": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 1,
            "maximum": 86400,
            "description": "After how long paused its instance has its memory saved and sleeps, from 1 to 86400 (300, Neon's five minutes, where none is given): paused, it holds its sessions in memory and a connection is answered in a millisecond or two, its memory metered; asleep, only its storage is, and it still keeps its sessions. Its node holds it to the longest it holds any instance paused (ten minutes). On Free 300, fixed: another value is refused 400 `invalid_field`."
          },
          "wake_daily_at": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^([01][0-9]|2[0-3]):[0-5][0-9]$",
            "description": "A time of day in UTC, `HH:MM`, at which the database is woken every day while it rests, for what waits for a wake (pg_partman's maintenance, a vacuum that is due); it rests again once that is done. Its `pg_cron` jobs wake it at their own times whatever this says. Null (the default): none."
          },
          "metadata": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": {
              "type": "string"
            }
          },
          "tiering": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": false,
            "properties": {
              "enabled": {
                "type": [
                  "boolean",
                  "null"
                ],
                "description": "On or off (the default)."
              }
            },
            "description": "Tiered storage (DB18, Tiger's way): partitions of a table partitioned by range that a policy (`glac.add_tiering_policy`) or a call (`glac.tier_partition`) moves become Parquet files in the platform's bucket of the project, still in the same table, read-only, read where `glac.enable_tiered_reads` is on. On a paid plan alone (Free: 403 `paid_plan_required`), where the object gateway serves (400 `objects_not_served` otherwise). Billed by their sizes before the move, $0.02 a GB-month (`tiered_bytes`); reading them is neither a request nor egress."
          },
          "history_seconds": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "maximum": 2592000,
            "description": "How far back its root branch can be restored, or branched from, by a time, in seconds (slice DB8): a day unless the create said (`history_seconds`), 0 (none) to 30 days on a paid plan, a day on Free (fixed, with a GB of old data at the most: its oldest versions go past it). The last day of it is its root branch's Drive's own window of versions, one every 30 seconds in which it was written (`POST /v1/databases/{id}/branches/{branch}/restore` with `at`); further back, and to the second, is its WAL archive's (slice DB5: `exact`). Not billed: the history of a database's last day is free."
          }
        }
      },
      "Database": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "id",
          "name",
          "engine",
          "version",
          "region",
          "status",
          "error",
          "default_branch",
          "database_name",
          "role",
          "password",
          "sleep_after_seconds",
          "suspend_after_seconds",
          "wake_daily_at",
          "metadata",
          "final_snapshot",
          "created_at",
          "tiering",
          "history_seconds",
          "history"
        ],
        "description": "A managed Postgres: an app of one stateful service and a Drive under it, which the control plane made and changes alone (`managed_by`).",
        "properties": {
          "object": {
            "type": "string",
            "const": "database"
          },
          "id": {
            "type": "string",
            "pattern": "^db-[0-9a-hjkmnp-tv-z]{16}$"
          },
          "name": {
            "type": "string",
            "description": "The project's one database of the name; its name on the project's network."
          },
          "engine": {
            "type": "string",
            "enum": [
              "postgres"
            ]
          },
          "version": {
            "type": "string",
            "enum": [
              "16",
              "17",
              "18"
            ],
            "description": "PostgreSQL's major version."
          },
          "region": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "na",
              "eu",
              "ap",
              null
            ],
            "description": "As the create named it; null: the project's home."
          },
          "status": {
            "type": "string",
            "enum": [
              "creating",
              "ready",
              "failed",
              "deleting",
              "deleted"
            ]
          },
          "error": {
            "type": [
              "string",
              "null"
            ],
            "description": "Why its first start failed, of a `failed` one."
          },
          "default_branch": {
            "$ref": "#/components/schemas/DatabaseBranch"
          },
          "database_name": {
            "type": "string",
            "description": "The database in the cluster (`glacdb` unless the create named another)."
          },
          "role": {
            "type": "string",
            "description": "Its owner, `<database_name>_owner`: a member of `glac_superuser`, no superuser."
          },
          "password": {
            "type": [
              "string",
              "null"
            ],
            "description": "The owner's password, in the answers to a create and a reset alone; null otherwise. Kept nowhere."
          },
          "sleep_after_seconds": {
            "type": "integer",
            "description": "After how long without anything asked of it its instance sleeps."
          },
          "suspend_after_seconds": {
            "type": [
              "integer",
              "null"
            ],
            "description": "After how long paused its instance (and each branch's) has its memory saved and sleeps (its node holds it to ten minutes). Null for a database made before it: its node's longest."
          },
          "wake_daily_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "The time of day in UTC at which it is woken every day while it rests (each branch too); null: none."
          },
          "metadata": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "final_snapshot": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": false,
            "required": [
              "id",
              "expires_at"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "expires_at": {
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "description": "Of a deleted one: the snapshot of its Drive kept, a Drive snapshot of the project's, and when it is deleted."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "tiering": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "enabled",
              "tiered_bytes"
            ],
            "properties": {
              "enabled": {
                "type": "boolean"
              },
              "tiered_bytes": {
                "type": "integer",
                "description": "What tiered storage bills by now: the sizes before the moves (`pg_table_size` of each partition as it was swapped) of the files its branches point at as tiered, each file once however many branches point at it, and never less than what its prefix holds in the object store. 0 while nothing is tiered."
              }
            },
            "description": "Tiered storage: whether it is on, and what it bills by now."
          },
          "history_seconds": {
            "type": "integer",
            "minimum": 0,
            "maximum": 2592000,
            "description": "How far back its root branch can be restored, or branched from, by a time (slice DB8): a day unless the create said (`history_seconds`), 0 (none) to 30 days on a paid plan, a day on Free (fixed, with a GB of old data at the most: its oldest versions go past it). The last day of it is its root branch's Drive's own window of versions, one every 30 seconds in which it was written (`POST /v1/databases/{id}/branches/{branch}/restore` with `at`); further back, and to the second, is its WAL archive's (slice DB5: `exact`). Not billed: the history of a database's last day is free."
          },
          "history": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "from",
              "stored_bytes",
              "gaps"
            ],
            "description": "Its history as it stands.",
            "properties": {
              "from": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time",
                "description": "The earliest time its root branch can be restored to now, by its Drive's window or its WAL archive, whichever reaches further back (within `history_seconds`); its making, where its WAL is archived (its archive's first base is the image's seed it started from); otherwise null until its Drive's window has a version (about three minutes after the database is ready)."
              },
              "stored_bytes": {
                "type": "integer",
                "format": "int64",
                "minimum": 0,
                "description": "What the versions of its root branch's window keep that its newest version does not (not billed: its last day is free)."
              },
              "gaps": {
                "type": "array",
                "description": "The gaps of its WAL archive within its history, the oldest first: where its archive's gateway refused its WAL so long that more of it waited than its Drive keeps for the log, and the WAL was given up so that the database stayed up. A time in one is restored only from its root branch's Drive's window (a whole minute, or a version's own time, while the window holds it); to the second or `exact`, it is refused (400 `invalid_history`), never taken from a version before it. Empty: none.",
                "items": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "from",
                    "to",
                    "from_lsn",
                    "to_lsn"
                  ],
                  "properties": {
                    "from": {
                      "type": "string",
                      "format": "date-time",
                      "description": "When the WAL given up began."
                    },
                    "to": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "format": "date-time",
                      "description": "Until when the archive restores no time: the end of the first base taken after the gap (taken as soon as the archive takes the WAL again, while the database is awake); null until there is one."
                    },
                    "from_lsn": {
                      "type": "string",
                      "description": "Where in the log the WAL given up begins (`0/5000000`)."
                    },
                    "to_lsn": {
                      "type": "string",
                      "description": "Where it ends: the archive holds the WAL again from there."
                    }
                  }
                }
              }
            }
          }
        }
      },
      "DatabaseBranch": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "id",
          "name",
          "parent",
          "endpoint",
          "state",
          "host",
          "pooler_host",
          "connection_uri",
          "pooled_uri",
          "private_host",
          "private_uri",
          "size",
          "storage",
          "app_id",
          "instance_id",
          "expires_at",
          "created_at",
          "start_command"
        ],
        "description": "A branch of a database: a stateful instance and a Drive. The root branch, `main`, or one of the database's own (slice DB6): a fork of its parent's Drive.",
        "properties": {
          "object": {
            "type": "string",
            "const": "branch"
          },
          "id": {
            "type": "string",
            "pattern": "^br-[0-9a-hjkmnp-tv-z]{16}$"
          },
          "name": {
            "type": "string"
          },
          "parent": {
            "type": [
              "string",
              "null"
            ],
            "description": "The branch it was made from, by its id: none, of the root branch."
          },
          "endpoint": {
            "type": "string",
            "pattern": "^ep-[a-z]+-[a-z]+-[0-9a-f]{8}$",
            "description": "Its endpoint's name, as Neon names one: random, so that no name of the database's or the project's is in an address."
          },
          "state": {
            "type": "string",
            "enum": [
              "creating",
              "failed",
              "awake",
              "paused",
              "asleep",
              "unavailable",
              "deleting",
              "deleted"
            ],
            "description": "Its instance's: `awake` while it runs, `paused` while the pause tier holds it in memory, `asleep` while it is saved; `unavailable` where its node cannot be reached (or it has no instance); `creating` and `failed` while the database has none yet, or failed to make one. `deleting` and `deleted` while it is deleted, and once it is."
          },
          "host": {
            "type": [
              "string",
              "null"
            ],
            "description": "Its public address, its endpoint under its database's region and the zone: `<endpoint>.<region>.<zone>` (`ep-calm-river-1a2b3c4d.na.db.glac.run`), port 5432, TLS alone; where an edge serves PostgreSQL (the control plane's `--database-zone`), null where none does or the database has no region to be named under."
          },
          "pooler_host": {
            "type": [
              "string",
              "null"
            ],
            "description": "Its pooler's public address (PgBouncer in its instance): `<endpoint>-pooler.<region>.<zone>`, port 5432; where an edge serves PostgreSQL (the control plane's `--database-zone`), null where none does or the database has no region to be named under."
          },
          "connection_uri": {
            "type": [
              "string",
              "null"
            ],
            "description": "How a client connects at its public address: `postgresql://<owner>@<host>/<database>?sslmode=require`, with the owner's password in the answers that give it (a create, a reset) alone; a branch of its own has its parent's password; where an edge serves PostgreSQL (the control plane's `--database-zone`), null where none does or the database has no region to be named under."
          },
          "pooled_uri": {
            "type": [
              "string",
              "null"
            ],
            "description": "The same at its pooler: `postgresql://<owner>@<pooler_host>/<database>?sslmode=require`; where an edge serves PostgreSQL (the control plane's `--database-zone`), null where none does or the database has no region to be named under."
          },
          "private_host": {
            "type": "string",
            "description": "Its name on the project's network: the database's name, of the root branch; `<database>-<branch>` of a branch of its own."
          },
          "private_uri": {
            "type": "string",
            "description": "How the project's sandboxes and services connect: `postgresql://<owner>@<private_host>:5432/<database>`, with the owner's password in the answers that give it (a create, a reset) alone. A branch of its own has its parent's roles and passwords."
          },
          "size": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "vcpus",
              "memory_bytes"
            ],
            "properties": {
              "vcpus": {
                "type": "integer"
              },
              "memory_bytes": {
                "type": "integer"
              }
            },
            "description": "Its instance's upper bounds; what it uses is billed."
          },
          "storage": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "drive_id",
              "size_bytes",
              "used_bytes",
              "billed_bytes",
              "durability"
            ],
            "properties": {
              "drive_id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "size_bytes": {
                "type": [
                  "integer",
                  "null"
                ],
                "description": "As the create gave it; null: the Drive's default."
              },
              "durability": {
                "type": "string",
                "enum": [
                  "strict",
                  "standard"
                ],
                "description": "Its Drive's tier, as its node says it: `strict` (a commit returns once the object store has it) or `standard`."
              },
              "used_bytes": {
                "type": [
                  "integer",
                  "null"
                ],
                "description": "What its Drive holds, as its node said last; null before it said."
              },
              "billed_bytes": {
                "type": [
                  "integer",
                  "null"
                ],
                "description": "What its Drive is billed for keeping: what it holds, of the root branch; of a branch of its own, what it changed since the fork (what its newest version reads from objects of its own in the store, as the collector last worked it out), never more than what it holds: 0 until the collector said. Null before anything was said."
              }
            },
            "description": "Its Drive (`GET /v1/drives/{drive_id}`)."
          },
          "app_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Its app (`GET /v1/apps/{app_id}`), once made."
          },
          "instance_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Its instance, a sandbox (`/v1/sandboxes/{instance_id}`: read it, run a command in it), where it has one."
          },
          "start_command": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/InstanceStartCommand"
              },
              {
                "type": "null"
              }
            ],
            "description": "How its server runs, as its instance's node says (the instance's `start_command`, `SandboxStartCommand` in `sandboxd/openapi/openapi.json`): `status` (`running`; `restarting`: it ended, and is started again at `restarting_at`; `exited`), `restarts` (how often it was started again in its machine after it ended, with its data as it was: PostgreSQL recovers by its WAL), `last_exit` (`status`, `signal`, `at`), `restarting_at`. Each start is also an event `instance.restarted`. Null where it has no instance, or its node said nothing of it yet."
          },
          "expires_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When it is deleted by itself (once no branch made of it is there): never, of the root branch, and of one made without it."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "DatabaseList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "data",
          "next_cursor"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Database"
            }
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "DatabaseUpdate": {
        "type": "object",
        "additionalProperties": false,
        "description": "What to change of a database; every field may be left out (nothing changes). Its size, `sleep_after_seconds`, its allowed addresses and its metadata are changed by later slices (refused as `unknown_field`, saying so).",
        "properties": {
          "suspend_after_seconds": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 1,
            "maximum": 86400,
            "description": "After how long paused each branch's instance has its memory saved and sleeps, as on a create (its node holds it to ten minutes); null: 300, what a create gives where none is said. On Free 300, fixed, as on a create: another value is refused 400 `invalid_field`."
          },
          "wake_daily_at": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^([01][0-9]|2[0-3]):[0-5][0-9]$",
            "description": "A time of day in UTC, `HH:MM`, at which each branch is woken every day while it rests, as on a create; null: none."
          },
          "history_seconds": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "maximum": 2592000,
            "description": "How far back its root branch can be restored (as on a create): its Drive's window is set to it at once; what a shorter one no longer keeps goes at the object store's collector's next round."
          },
          "tiering": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": false,
            "properties": {
              "enabled": {
                "type": [
                  "boolean",
                  "null"
                ]
              }
            },
            "description": "Tiered storage on (a paid plan, where the object gateway serves) or off (refused while a branch has partitions tiered or queued: 409 `tiered_data_exists`). Each branch's PostgreSQL restarts once for it (its app is deployed again)."
          }
        }
      },
      "DatabaseBranchList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "data",
          "next_cursor"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DatabaseBranch"
            }
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "DatabaseBranchCreate": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "name"
        ],
        "description": "A branch of a database's own (slice DB6).",
        "properties": {
          "name": {
            "type": "string",
            "description": "What it is called in the database: lower-case letters, digits and hyphens, beginning with a letter; not `main`. Its name on the project's network is `<database>-<branch>`, 40 characters at the most."
          },
          "parent": {
            "type": [
              "string",
              "null"
            ],
            "description": "The branch it is made of, by its name or its id: the root branch, `main`, unless it names another."
          },
          "expires_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When it is deleted by itself, a moment to come; null: never."
          },
          "from": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": false,
            "required": [
              "at"
            ],
            "description": "What it is made of, where it is not its parent as it is now (slices DB8 and DB5): `at`, a time within the root branch's history (`history_seconds`; the root alone keeps one), as a restore takes it: the newest version of the root's Drive's window at or before it (one every 30 seconds in which it was written, which PostgreSQL recovers as after a power cut) where `at` is a whole minute or that version's own moment; otherwise (further back than the window holds, to the second, or with `exact`) `at` itself, to the millisecond, by the root's WAL archive (its WAL replayed to it and promoted before the branch's instance starts), or refused where the archive does not reach it (400 `invalid_history`; 503 `database_archive_unavailable` while its store refuses its segments), the refusal saying the window's version at or before it: never that version in its place. A snapshot (`snapshot`) is a later slice's.",
            "properties": {
              "at": {
                "type": "string",
                "format": "date-time"
              },
              "exact": {
                "type": "boolean",
                "description": "true: `at` itself, to the millisecond, by the WAL archive, whatever its precision."
              }
            }
          }
        }
      },
      "DatabaseBranchRestore": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "at"
        ],
        "description": "The time to restore a database's root branch to.",
        "properties": {
          "at": {
            "type": "string",
            "format": "date-time",
            "description": "A time within its history (`history_seconds`). A whole minute, or a version's own moment (`GET /v1/drives/{id}/versions`), within its root branch's Drive's window (a day at the most): that window's newest version at or before it, one every 30 seconds in which it was written. Further back than the window holds, or to the second: the moment itself, to the millisecond, by its WAL archive (slice DB5: the newest base before it, its WAL replayed to it), or refused where the archive does not reach it (400 `invalid_history`; 503 `database_archive_unavailable` while its store refuses its segments), the refusal saying the window's version at or before it: never that version in its place."
          },
          "exact": {
            "type": "boolean",
            "description": "true: the moment `at` itself, to the millisecond, by the WAL archive, whatever its precision (a whole minute too). false or none: as `at` says."
          }
        }
      },
      "DatabaseRoleList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "data",
          "next_cursor"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "list"
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DatabaseRole"
            }
          },
          "next_cursor": {
            "type": "null"
          }
        }
      },
      "DatabaseRole": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "name",
          "owner"
        ],
        "description": "A role of a database's root branch that logs in.",
        "properties": {
          "object": {
            "type": "string",
            "const": "database_role"
          },
          "name": {
            "type": "string"
          },
          "owner": {
            "type": "boolean",
            "description": "Whether it is the database's owner (`role` of the database)."
          }
        }
      },
      "DatabaseRoleSecret": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "database_id",
          "name",
          "owner",
          "password",
          "connection_uri",
          "pooled_uri",
          "private_uri"
        ],
        "description": "A role of a database other than its owner, with the new password a reset gave it: in this answer alone, kept nowhere.",
        "properties": {
          "object": {
            "type": "string",
            "const": "database_role"
          },
          "database_id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "owner": {
            "type": "boolean",
            "const": false
          },
          "password": {
            "type": "string",
            "description": "The new password: letters and digits, which a connection string takes as they are."
          },
          "connection_uri": {
            "type": [
              "string",
              "null"
            ],
            "description": "The root branch's public address with this role and its password (`sslmode=require`); null where no edge serves PostgreSQL."
          },
          "pooled_uri": {
            "type": [
              "string",
              "null"
            ],
            "description": "The same at its pooler."
          },
          "private_uri": {
            "type": [
              "string",
              "null"
            ],
            "description": "Its address on the project's network, port 5432, with this role and its password."
          }
        }
      },
      "VolumeCreate": {
        "type": "object",
        "additionalProperties": false,
        "description": "A volume to make. Every field may be left out.",
        "properties": {
          "name": {
            "type": [
              "string",
              "null"
            ],
            "description": "1 to 63 lower-case letters, digits and hyphens, not beginning with an id's prefix: the project's for one volume (or volume) at a time."
          },
          "size_bytes": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int64",
            "minimum": 16777216,
            "description": "The most it holds: a whole number of 4 KiB blocks (a multiple of 4096), at least 16 MiB, at most what the account's plan lets one be (10 GiB on Free, 1 TiB on Paid, up to 16 TiB for an enterprise account by an operator's number). Not given: 64 GiB, or 5 GiB on Free (an even share of its 15 GiB together, so that three of no size fit). Only what is written is billed."
          },
          "region": {
            "type": [
              "string",
              "null"
            ],
            "x-extensible-enum": [
              "na",
              "eu",
              "ap"
            ],
            "description": "Where it is made: the control plane's own region unless given. A sandbox in another region does not mount it (D9)."
          },
          "durability": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "standard",
              "strict",
              null
            ],
            "default": "standard",
            "description": "How it is kept (`volume.durability`): `standard` (the default), or `strict`, whose guest's fsync returns once the object store holds every write before it. Kept for good. A strict volume is mounted only by a sandbox made from a template built since strict volumes (409 `strict_unsupported` otherwise)."
          },
          "history_seconds": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int64",
            "minimum": 0,
            "maximum": 86400,
            "description": "How far back the volume can be restored, or forked, by a time (its window of versions, slice DB8: reports/storage/drives.md 4.7): 3600 to 86400 seconds (an hour to a day). Every window (30 seconds) a volume that was written gets a version that is the volume at one moment (its machine paused for the last of it, two seconds at the most), kept for this long; what they keep that the volume no longer needs is billed as a snapshot's is (`history.exclusive_bytes`, on the meter of storage under `dhist-<the volume's id without vol->`). Null (or 0): none, the default; set to null, what it kept goes."
          },
          "cache_bytes": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int64",
            "minimum": 0,
            "description": "Its cache on its node's NVMe (`volume.cache_bytes`): a whole number of 4 KiB blocks (a multiple of 4096) from 0 to its size (400 `invalid_field` otherwise). Not given, or null: all of it. One smaller than the volume is served by its version from the start, so that a sandbox that mounts it holds it to its cache."
          },
          "shared": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "True: a shared volume (`kind` `shared`), a file system its node keeps, which any number of sandboxes have at once; it is made and answered by its node as before (none of `cache_bytes`, `durability`, `history_seconds` with it)."
          },
          "kind": {
            "type": [
              "string",
              "null"
            ],
            "x-extensible-enum": [
              "drive",
              "shared"
            ],
            "description": "`drive` (the default) or `shared` (as `shared: true`). A project's key makes no `plain` one (400 `invalid_field`)."
          },
          "from_backup": {
            "type": [
              "string",
              "null"
            ],
            "description": "A backup to make it from (`bak-…`): of the backup's volume's size and kind, in the backup's region, `restoring` until it holds the backup whole, then `ready` (volume_backups.rs)."
          }
        }
      },
      "Volume": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id",
          "object",
          "name",
          "size_bytes",
          "used_bytes",
          "attached_to",
          "region",
          "home",
          "home_move",
          "status",
          "lease",
          "app_id",
          "key",
          "parent_volume_id",
          "root_volume_id",
          "created_at",
          "managed_by",
          "history_seconds",
          "history",
          "cache_bytes",
          "cached_bytes",
          "attachments",
          "kind",
          "shared",
          "host_failure",
          "from_backup",
          "failure",
          "restore_attempt",
          "first_failure",
          "placement_refusals",
          "backups"
        ],
        "description": "A volume: a disk of the project's that outlives its sandboxes, mounted read-write by one of them at a time; its snapshots, read-only, by any number. Kept in the object store whole (its data, billed by what it holds: `used_bytes`), and on its node's NVMe as far as its cache goes (`cache_bytes`). `/v1/drives` answers the same calls as before the rename, its objects Drives (`object` `drive`, `attached_to` the mounts, `parent_drive_id`, `root_drive_id`), for the clients from before it.",
        "properties": {
          "managed_by": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": false,
            "required": [
              "object",
              "id"
            ],
            "properties": {
              "object": {
                "type": "string",
                "enum": [
                  "database",
                  "installation"
                ]
              },
              "id": {
                "type": "string"
              }
            },
            "description": "The database it is under (`/v1/databases`), or the installation of a Managed Application it is part of (`/v1/installations`), which changes it alone: every other call that would change it is refused, 409 `managed_by_database` or `managed_by_installation`. Null: none's."
          },
          "id": {
            "type": "string",
            "pattern": "^vol-[0-9a-hjkmnp-tv-z]{16}$",
            "description": "Its id."
          },
          "object": {
            "type": "string",
            "const": "volume"
          },
          "name": {
            "type": [
              "string",
              "null"
            ]
          },
          "size_bytes": {
            "type": "integer",
            "format": "int64",
            "minimum": 16777216,
            "description": "The most it holds: a whole number of 4 KiB blocks."
          },
          "used_bytes": {
            "type": "integer",
            "format": "int64",
            "minimum": 0,
            "description": "What it holds, logically: the 4 KiB blocks written to it and not discarded since (a guest's `fstrim`, which its node asks for every minute, gives them back), its file system's own among them. What it is billed by (`storage_gb_hours`, $0.05 a GB-month); a fork too, by all it holds, what its snapshot holds among it: a fork of a 10 GB snapshot is billed as a volume of 10 GB, whatever it shares with the snapshot and its volume in the object store. As its node said last; while no node holds it, the last the control plane heard."
          },
          "attached_to": {
            "type": "array",
            "items": {
              "type": "string",
              "pattern": "^sbx-[0-9a-hjkmnp-tv-z]{16}$"
            },
            "description": "The sandboxes that have it, by their ids, as the volumes' API always said them: one at the most (`attachments` says where and how)."
          },
          "region": {
            "oneOf": [
              {
                "type": "string",
                "x-extensible-enum": [
                  "na",
                  "eu",
                  "ap"
                ]
              },
              {
                "type": "null"
              }
            ]
          },
          "home": {
            "oneOf": [
              {
                "type": "string",
                "x-extensible-enum": [
                  "na",
                  "eu",
                  "ap"
                ]
              },
              {
                "type": "null"
              }
            ],
            "description": "The region whose object store holds its copy: the region it was made in (`region`), until it moves to the region it is mounted in (`POST /v1/volumes/{id}/home`, or by itself after an hour there). Null where its node copies nothing to an object store. Null of a volume of another kind than a Drive (`kind`)."
          },
          "home_move": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": false,
            "required": [
              "asked_at",
              "asked_by",
              "copied_objects",
              "copied_bytes",
              "objects_left",
              "error"
            ],
            "description": "A move of its home to the region it is held in, while one is under way: when it was asked and why (`user`: `POST /v1/volumes/{id}/home`; `away`: held there for an hour; `back`: held in its home's region again, taken from another region's store before its home moved there: what it wrote there is copied home, and its home stays), how many of its objects and bytes are copied so far and how many objects are left, and the error it stopped at last, where it stopped (it goes on by itself). Null where none is under way. Null of a volume of another kind than a Drive (`kind`).",
            "properties": {
              "asked_at": {
                "type": "string",
                "format": "date-time"
              },
              "asked_by": {
                "type": [
                  "string",
                  "null"
                ],
                "x-extensible-enum": [
                  "user",
                  "away",
                  "back"
                ]
              },
              "copied_objects": {
                "type": [
                  "integer",
                  "null"
                ],
                "minimum": 0
              },
              "copied_bytes": {
                "type": [
                  "integer",
                  "null"
                ],
                "format": "int64",
                "minimum": 0
              },
              "objects_left": {
                "type": [
                  "integer",
                  "null"
                ],
                "minimum": 0
              },
              "error": {
                "type": [
                  "string",
                  "null"
                ]
              }
            }
          },
          "status": {
            "type": "string",
            "x-extensible-enum": [
              "ready",
              "restoring",
              "failed",
              "unavailable"
            ],
            "description": "`ready`; `restoring`: made from a backup and not all here yet; `failed`: that restore gave up, and it can only be deleted; `unavailable`: its node cannot be reached now (it is as it was last seen)."
          },
          "lease": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/VolumeLease"
              },
              {
                "type": "null"
              }
            ],
            "description": "While a sandbox has it read-write; null otherwise. Null of a volume of another kind than a Drive (`kind`)."
          },
          "app_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "A keyed instance's volume (`key_drives` of the app's version): the app. Null of a volume of another kind than a Drive (`kind`)."
          },
          "key": {
            "type": [
              "string",
              "null"
            ],
            "description": "And the key whose volume it is: it is made with the key's instance, has no other sandbox, and is deleted with the key. Null of a volume of another kind than a Drive (`kind`)."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "cache_bytes": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int64",
            "minimum": 0,
            "description": "Its cache on its node's NVMe: the most of it the node keeps there, its size where that is all of it (the default); the rest is in the object store alone, and a read of it fetches it (the guest waits for the store) and keeps it, while what was used least long ago goes once the store holds it (never what it does not hold yet, which may take a volume past its cache for as long as the store takes to have it). Under 64 MiB it is held at 64 MiB, what a guest's read brings with its read-ahead. Billed by the hour as the volume's NVMe cache, up to what it holds (`cache_gb_hours` of `GET /v1/usage`), beside what it holds (`used_bytes`, `storage_gb_hours`): all of it cached costs both, none costs the second alone. A database's caches a quarter of it, 1 GiB at the least. A fork has the cache of the volume it was made of. Null of a volume of another kind than a Drive (`kind`)."
          },
          "cached_bytes": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int64",
            "minimum": 0,
            "description": "What of it is on its node's NVMe now: what was written to it and read of it and not evicted since. Null while no node holds it, and from a node from before caches. Null of a volume of another kind than a Drive (`kind`)."
          },
          "durability": {
            "type": [
              "string",
              "null"
            ],
            "x-extensible-enum": [
              "standard",
              "strict"
            ],
            "description": "How it is kept, decided when it is made (`DriveCreate.durability`). `standard`: a guest's fsync returns at once (the write is on its node), and a host that is lost loses at most the last second of writes. `strict`: a guest's fsync, and every flush of its file system, returns once the object store holds every write before it: nothing a guest synced is lost with its host, and each fsync waits for a request to the store (tens of milliseconds). Null of a volume of another kind than a Drive (`kind`)."
          },
          "history_seconds": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int64",
            "minimum": 3600,
            "maximum": 86400,
            "description": "How far back the volume can be restored, or forked, by a time (its window of versions, slice DB8: reports/storage/drives.md 4.7): 3600 to 86400 seconds (an hour to a day). Every window (30 seconds) a volume that was written gets a version that is the volume at one moment (its machine paused for the last of it, two seconds at the most), kept for this long; what they keep that the volume no longer needs is billed as a snapshot's is (`history.exclusive_bytes`, on the meter of storage under `dhist-<the volume's id without vol->`). Null (or 0): none, the default; set to null, what it kept goes. Null of a volume of another kind than a Drive (`kind`)."
          },
          "history": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": false,
            "required": [
              "from",
              "versions",
              "exclusive_bytes",
              "exclusive_at",
              "keep_bytes"
            ],
            "description": "Its window of versions while it has one (`history_seconds`); null otherwise. Null of a volume of another kind than a Drive (`kind`).",
            "properties": {
              "from": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time",
                "description": "The earliest time it can be restored to: the window's beginning, or its first version where that is later; null until it has one."
              },
              "versions": {
                "type": "integer",
                "minimum": 0,
                "description": "How many versions the window keeps (`GET /v1/volumes/{id}/versions`)."
              },
              "exclusive_bytes": {
                "type": "integer",
                "format": "int64",
                "minimum": 0,
                "description": "What its versions keep alone: the data they read from objects that the volume's newest version, its snapshots and every other volume need not, as the object store's collector last worked it out. What the window is billed for, as a snapshot is."
              },
              "exclusive_at": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time",
                "description": "When the collector worked that out; null: not yet."
              },
              "keep_bytes": {
                "type": [
                  "integer",
                  "null"
                ],
                "format": "int64",
                "minimum": 0,
                "description": "The most old data the window keeps (a Free plan's database: a GB, as Tiger keeps): its oldest versions go past it. Null: no bound."
              }
            }
          },
          "attachments": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/VolumeAttachment"
            },
            "description": "The sandbox that has it, where and how: one at the most. Empty for a volume of another kind than a Drive."
          },
          "parent_volume_id": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^vol-[0-9a-hjkmnp-tv-z]{16}$",
            "description": "The volume this one was forked from (`POST /v1/volumes/{id}/fork`), which may be deleted since; null for a volume that is no fork. Null of a volume of another kind than a Drive (`kind`)."
          },
          "root_volume_id": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^vol-[0-9a-hjkmnp-tv-z]{16}$",
            "description": "The first volume of its line of forks (the parent's root, or the parent where that is no fork); null for a volume that is no fork. Null of a volume of another kind than a Drive (`kind`)."
          },
          "kind": {
            "type": "string",
            "x-extensible-enum": [
              "drive",
              "shared",
              "plain"
            ],
            "description": "`drive`: a volume as this section says it, a disk one sandbox has at a time, copied into the object store (every volume made for a project's key but a shared one). `shared`: a file system a node keeps, any number of sandboxes have at once (`POST /v1/volumes` with `shared: true`), which is none of the rest: no lease, no home, no snapshots, forks or versions, no cache of its own. `plain`: one the operator made (a file system on one node, never copied)."
          },
          "shared": {
            "type": "boolean",
            "description": "Whether it is a shared volume (`kind` `shared`)."
          },
          "host_failure": {
            "type": [
              "object",
              "null"
            ],
            "description": "What the last hard stop of its node's host left uncertain about its disk, as its node says it; null: nothing."
          },
          "from_backup": {
            "type": [
              "string",
              "null"
            ],
            "description": "The backup it was made from (`POST /v1/volumes` with `from_backup`); null for one made empty."
          },
          "failure": {
            "type": [
              "string",
              "null"
            ],
            "description": "Why it could not be made from its backup (`status` `failed`); null otherwise."
          },
          "restore_attempt": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 1,
            "maximum": 2,
            "description": "Of one made from a backup, which attempt to make it this is (2: the first failed on another node); null for one made empty."
          },
          "first_failure": {
            "type": [
              "string",
              "null"
            ],
            "description": "With `restore_attempt` 2, why the first attempt failed; null otherwise."
          },
          "placement_refusals": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "node",
                "code"
              ],
              "properties": {
                "node": {
                  "type": "string"
                },
                "code": {
                  "type": "string"
                }
              }
            },
            "description": "Of one made from a backup: the nodes that refused to make it before one did, and why; null where the first did."
          },
          "backups": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": false,
            "properties": {
              "daily": {
                "type": "boolean"
              },
              "keep": {
                "type": "integer"
              },
              "expires_in_days": {
                "type": "integer"
              }
            },
            "description": "Its rule for its backups (`PATCH /v1/volumes/{id}` with `backups`; its backups: `/v1/volumes/{id}/backups`): whether one is taken every day, how many of those are kept, and for how long one is kept. Null where its node says none."
          }
        }
      },
      "VolumeLease": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "node_id",
          "epoch",
          "expires_at"
        ],
        "description": "The lease of the volume's one writer (C7's): the node that holds it, the epoch it holds it at (each node that takes the volume holds it one epoch above every claim the object store has of it, which fences the one before), and when that node's lease runs out unless it reports again. A node that cannot renew its lease stops every machine it has with a volume attached before then, and nothing writes the volume from that moment.",
        "properties": {
          "node_id": {
            "type": "string"
          },
          "epoch": {
            "type": "integer",
            "format": "int64",
            "minimum": 1
          },
          "expires_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Null: the control plane has granted the node no lease since it started."
          }
        }
      },
      "VolumeAttachment": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "sandbox_id",
          "path",
          "mode"
        ],
        "description": "A sandbox that has the volume, where, and how.",
        "properties": {
          "sandbox_id": {
            "type": "string",
            "pattern": "^sbx-[0-9a-hjkmnp-tv-z]{16}$"
          },
          "path": {
            "type": [
              "string",
              "null"
            ],
            "description": "Where the sandbox has it mounted: an absolute path. Null where the control plane has heard of the mount from the node's inventory alone."
          },
          "mode": {
            "type": "string",
            "x-extensible-enum": [
              "read_write"
            ],
            "description": "`read_write`: the one writer. A read-only mount of one of its snapshots is not one of these: it is a volume of the sandbox's own (`view_of`), taken away by the snapshot's id."
          }
        }
      },
      "VolumeList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "data",
          "next_cursor"
        ],
        "description": "The project's volumes, newest first.",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Volume"
            }
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Where the next page begins; null on the last."
          }
        }
      },
      "VolumeUpdate": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "name": {
            "type": [
              "string",
              "null"
            ],
            "description": "Its new name, or null for none."
          },
          "cache_bytes": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int64",
            "minimum": 0,
            "description": "Its cache on its node's NVMe (`volume.cache_bytes`) changed: a whole number of 4 KiB blocks from 0 to its size (400 `invalid_field` otherwise), or null for all of it. Its node holds it to a smaller one from its next look (a second); one a sandbox has that is not served by its version yet (it was made with all of it cached, and is mounted), from the sandbox's next sleep or stop. Billed by the new one from then. Refused while no node holds it (503 `drive_in_store`)."
          },
          "history_seconds": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int64",
            "minimum": 0,
            "maximum": 86400,
            "description": "Its window of versions set, changed, or (null or 0) turned off: How far back the volume can be restored, or forked, by a time (its window of versions, slice DB8: reports/storage/drives.md 4.7): 3600 to 86400 seconds (an hour to a day). Every window (30 seconds) a volume that was written gets a version that is the volume at one moment (its machine paused for the last of it, two seconds at the most), kept for this long; what they keep that the volume no longer needs is billed as a snapshot's is (`history.exclusive_bytes`, on the meter of storage under `dhist-<the volume's id without vol->`). Null (or 0): none, the default; set to null, what it kept goes."
          },
          "backups": {
            "type": "object",
            "additionalProperties": false,
            "properties": {
              "daily": {
                "type": "boolean"
              },
              "keep": {
                "type": "integer",
                "minimum": 1,
                "maximum": 90
              },
              "expires_in_days": {
                "type": "integer",
                "minimum": 1,
                "maximum": 365
              }
            },
            "description": "Its rule for its backups (`Volume.backups`): what is given changes, the rest stays."
          }
        }
      },
      "VolumeSnapshotCreate": {
        "type": "object",
        "additionalProperties": false,
        "description": "A snapshot to take. Every field may be left out.",
        "properties": {
          "name": {
            "type": [
              "string",
              "null"
            ],
            "description": "1 to 63 lower-case letters, digits and hyphens, not beginning with an id's prefix: a word for it, which need not be the only one."
          },
          "fs_consistent": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "Freeze the file system on the volume for the last of it, where a running sandbox has it (`consistency` `frozen`): refused (409 `not_frozen`) where the sandbox's agent cannot. False, the default: its machine is paused for it instead."
          }
        }
      },
      "VolumeSnapshot": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id",
          "object",
          "volume_id",
          "name",
          "consistency",
          "used_bytes",
          "exclusive_bytes",
          "exclusive_at",
          "version",
          "created_at"
        ],
        "description": "A snapshot of a volume (slice D7): a version of it in the object store, kept; nothing of the volume is copied for it. It outlives its volume.",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^dsnap-[0-9a-hjkmnp-tv-z]{16}$"
          },
          "object": {
            "type": "string",
            "const": "volume_snapshot"
          },
          "name": {
            "type": [
              "string",
              "null"
            ]
          },
          "consistency": {
            "type": [
              "string",
              "null"
            ],
            "x-extensible-enum": [
              "frozen",
              "paused",
              "at_rest",
              null
            ],
            "description": "How the guest's writes were held for it: `frozen`, its file system frozen by the sandbox's agent (`fs_consistent`), consistent for the file system; `paused`, its machine paused, as at a power cut; `at_rest`, no running sandbox had the volume read-write."
          },
          "used_bytes": {
            "type": "integer",
            "format": "int64",
            "minimum": 0,
            "description": "What the volume held when it was taken (its `used_bytes`)."
          },
          "exclusive_bytes": {
            "type": "integer",
            "format": "int64",
            "minimum": 0,
            "description": "What it keeps alone: the data its version reads from objects that the volume's newest version, its newer snapshots and every fork need not (an object two snapshots keep counts to the older), as the store's collector last worked it out; 0 before that. What it is billed for, by the hour."
          },
          "exclusive_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the collector worked that out; null: not yet."
          },
          "version": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "number",
              "epoch"
            ],
            "description": "The version of the volume it is.",
            "properties": {
              "number": {
                "type": "integer",
                "format": "int64",
                "minimum": 0
              },
              "epoch": {
                "type": "integer",
                "format": "int64",
                "minimum": 0
              }
            }
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "volume_id": {
            "type": "string",
            "pattern": "^vol-[0-9a-hjkmnp-tv-z]{16}$",
            "description": "The volume it is of, which may be deleted since."
          }
        }
      },
      "VolumeSnapshotList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "data",
          "next_cursor"
        ],
        "description": "Snapshots of volumes, the newest first: a volume's (`GET /v1/volumes/{id}/snapshots`), or the project's (`GET /v1/volume-snapshots`).",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/VolumeSnapshot"
            }
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Null: every snapshot is on this page."
          }
        }
      },
      "VolumeFork": {
        "type": "object",
        "additionalProperties": false,
        "description": "A fork to make. Every field may be left out.",
        "properties": {
          "name": {
            "type": [
              "string",
              "null"
            ],
            "description": "The new volume's name: 1 to 63 lower-case letters, digits and hyphens, not beginning with an id's prefix, the project's for one volume at a time."
          },
          "from_snapshot": {
            "type": [
              "string",
              "null"
            ],
            "description": "The snapshot of the volume (`dsnap-…`) the fork is made of; not given, the volume as it is now."
          },
          "at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "A time within the volume's window of versions (`history_seconds`; not with `from_snapshot`): the fork is made of its newest version at or before then (`GET /v1/volumes/{id}/versions?at=`), the volume as it was at that version's moment."
          }
        }
      },
      "VolumeVersionList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "data",
          "next_cursor"
        ],
        "description": "The versions a volume's window keeps, the newest first; with `at`, the one a restore to that time takes.",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/VolumeVersion"
            }
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Null: every version is on this page."
          }
        }
      },
      "VolumeVersion": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "volume_id",
          "at",
          "version",
          "consistency",
          "exclusive_bytes"
        ],
        "description": "A version of a volume its window keeps (slice DB8): the volume as it was at one moment, which a restore or a fork to a time at or after it, before the next, takes.",
        "properties": {
          "object": {
            "type": "string",
            "const": "volume_version"
          },
          "at": {
            "type": "string",
            "format": "date-time",
            "description": "The moment it is the volume at."
          },
          "version": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "number",
              "epoch"
            ],
            "description": "The version of the volume it is.",
            "properties": {
              "number": {
                "type": "integer",
                "format": "int64",
                "minimum": 0
              },
              "epoch": {
                "type": "integer",
                "format": "int64",
                "minimum": 0
              }
            }
          },
          "consistency": {
            "type": [
              "string",
              "null"
            ],
            "x-extensible-enum": [
              "quiet",
              "at_rest",
              "paused",
              null
            ],
            "description": "How it was had: `quiet`, nothing written since the volume's newest version; `at_rest`, nothing wrote the volume (no running sandbox had it); `paused`, its sandbox's machine paused while the last of it was read, as at a power cut."
          },
          "exclusive_bytes": {
            "type": "integer",
            "format": "int64",
            "minimum": 0,
            "description": "What it keeps alone (an object two versions alone keep is the older's), as the collector last worked it out."
          },
          "volume_id": {
            "type": "string",
            "pattern": "^vol-[0-9a-hjkmnp-tv-z]{16}$"
          }
        }
      },
      "VolumeRestore": {
        "type": "object",
        "additionalProperties": false,
        "description": "What to restore a volume to: one of its snapshots, or a time within its window of versions; one of the two.",
        "properties": {
          "snapshot": {
            "type": [
              "string",
              "null"
            ],
            "description": "A snapshot of the volume (`dsnap-…`)."
          },
          "at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "A time within the volume's window of versions (`history_seconds`): the volume is restored to its newest version at or before then (`GET /v1/volumes/{id}/versions?at=`), as it was at that version's moment."
          }
        }
      },
      "VolumeMount": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "volume",
          "path"
        ],
        "description": "A volume for a sandbox to mount, read-write, or one of its snapshots, read-only.",
        "properties": {
          "volume": {
            "type": "string",
            "description": "The volume's id or name."
          },
          "path": {
            "type": "string",
            "description": "Where it is mounted: an absolute path other than `/`."
          },
          "mode": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "read_write",
              "snapshot",
              null
            ],
            "description": "`read_write` (the default): the one writer. `snapshot`: a read-only mount of the snapshot of the volume that `snapshot` names (slice D7), which any number of sandboxes on any node of the volume's region have at once (a sandbox is made in the volume's region for it; one on a node of another region is refused, 409 `not_on_one_machine`: D9 mounts elsewhere), whatever has the volume, and after the volume is deleted. What a sandbox reads of it from the object store is billed to its account as a volume's cold reads (`drive_cold_read_bytes`, under the sandbox in `GET /v1/usage`); what its node holds of it already is read for nothing."
          },
          "snapshot": {
            "type": [
              "string",
              "null"
            ],
            "description": "With `mode` `snapshot`, and with it alone: the snapshot of the volume to mount (`dsnap-…`)."
          }
        }
      },
      "RunOnceRequest": {
        "type": "object",
        "description": "The fields of `SandboxCreate` and those of a command in one object; `env` is both the sandbox's and the command's. `wait_seconds`, `stdin_open` and `tty` are not taken. `sleep_after_seconds` is accepted and ignored: the sandbox never sleeps.",
        "required": [
          "command"
        ],
        "properties": {
          "start": {
            "type": "boolean",
            "description": "`true`: start the image's own start command (ENTRYPOINT and CMD) when the machine boots; `false`: start nothing, whatever the image's labels ask for. Given (as any of `start`, `start_command` and `ready`), it is all that counts: the image's labels `sbx.start` and `sbx.ready` are not read. The sandbox is made from a template built with these settings and `env` (two settings of one image are two templates; the first sandbox of a setting is returned `starting` while it is built), which only its owner's sandboxes are made from. Not for a sandbox made from a `snapshot` or a `template` (400 `invalid_start`)."
          },
          "start_command": {
            "description": "The start command, run as the image's user, its output kept by the sandbox's agent as that of the command `exe-start` (the last 16 MiB of it, read like any command's with `GET …/exec/exe-start` and `…/exec/exe-start/output`; for an app's instance it is also what the app's log has of the instance): a string is run by `/bin/sh -c` and may hold several lines; a list is run as it is, without a shell (for an image that has none). At most 1000 arguments and 64 KiB, no NUL bytes, the first argument not empty. With `start: false`, 400 `invalid_start`.",
            "oneOf": [
              {
                "type": "string",
                "minLength": 1,
                "maxLength": 65536
              },
              {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "minItems": 1,
                "maxItems": 1000
              }
            ]
          },
          "ready": {
            "$ref": "#/components/schemas/NodeReady"
          },
          "egress": {
            "$ref": "#/components/schemas/NodeEgressRules",
            "description": "What the sandbox may reach, and which of its requests get a secret added, as in `SandboxCreate`."
          },
          "guest_mitigations": {
            "type": "boolean",
            "description": "Whether the guest kernel keeps its mitigations against speculative execution. They guard the guest's kernel against processes in the same sandbox: turn them on when a sandbox runs code of several users who do not trust each other. Sandboxes are kept apart from each other by the host either way. Default: the node's setting (off). The setting is fixed when the kernel boots: the first sandbox with a setting other than its image template's starts like a new image (returned `starting`), and a sandbox made from a snapshot keeps the snapshot's (asking for the other is 400)."
          },
          "tmp": {
            "type": "string",
            "enum": [
              "disk",
              "memory"
            ],
            "default": "disk",
            "description": "Where the sandbox's /tmp is. `disk`: on its disk, as the rest. `memory`: a tmpfs of at most half of its memory, mounted before its first command (in a sandbox made from a template or an image, over what the template's guest had put there; by the guest's init where it starts from its disk): what is written there is the guest's memory, billed as memory, kept when it sleeps, and gone when it starts again from its disk. A sandbox made from a snapshot whose sandbox had it in memory has it so whatever this says."
          },
          "runtime": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/NodeRuntime"
              },
              {
                "type": "null"
              }
            ],
            "description": "Runtime settings of its machine and its commands, as a Compose file's `ulimits`, `sysctls`, `tmpfs`, `dns` and `shm_size` ask for them (`Runtime`). For a sandbox made from an image: they make a template of the caller's own, built with them (with nothing said of the start, the image is started as its labels ask). Refused for one made from a snapshot or a template (400 `invalid_start`), which goes on with what its machine has."
          },
          "init": {
            "oneOf": [
              {
                "type": "string",
                "enum": [
                  "systemd"
                ]
              },
              {
                "type": "null"
              }
            ],
            "description": "What the sandbox's PID 1 is. Not given, or null: the platform's init, which starts the image's start command where there is one (`start`, `start_command`, the image's labels). `systemd`: a whole system. The image's own systemd is PID 1 and the node's agent is one of its services: the user installs packages, starts services with `systemctl`, uses cron and the journal and logs in by SSH, as on a machine. Nothing a create names is started (`start`, `start_command` and `ready` are refused with 400 `invalid_start`), and the template is saved once systemd says the machine has booted. Without `image`: the node's image of a whole system (`--systemd-image`; Ubuntu 24.04 with systemd and OpenSSH, sandboxd/images/ubuntu), refused with 400 `invalid_start` on a node that has none. An image without systemd (/usr/lib/systemd/systemd) fails its build, with `failure_code` `invalid_image`. Its template is anybody's who asks for the same image so, unless something of the caller's is in it (`runtime`, `image_pull_secret`). Refused for one made from a snapshot or a template (400 `invalid_start`), which goes on as its machine was. Everything else a sandbox does works as for any sandbox: commands, files, terminals, the SSH gateway, sleep and wake, snapshots and forks, moves, egress rules (sandboxd/README.md, 「整个系统：systemd 当 PID 1」)."
          },
          "project_network": {
            "type": "boolean",
            "description": "`true`: the sandbox is on its project's private network from before it runs anything, at the first of the addresses the node was left to spare for the project (`setProjectNetwork`): the answer says which (`project_address`). The project is the call's owner, so this is for a call the control plane makes for a project; on a node that stands alone a sandbox is nobody's, and is put on a network by being named in it. A sandbox is on no project's network unless it asks: default `false`. A copy made by a fork is on none, whatever its original is on."
          },
          "image": {
            "type": "string",
            "maxLength": 512,
            "description": "An OCI image reference (`python:3.12`, `ghcr.io/org/app@sha256:…`), in a registry on the internet: one that gives the image to anybody, or one that wants a login, with `image_pull_secret`. The first sandbox of an image is returned `starting` while the node pulls the image and builds a template; later ones start from the template. Default: the node's base image."
          },
          "image_pull_secret": {
            "type": "string",
            "maxLength": 128,
            "description": "The name of a secret (`PUT /v1/secrets/{name}`) that holds the credentials of the image's registry, for an image that is not public: `username:password` in printable ASCII, the user name ending at the first colon. A registry that takes a token alone documents the user name that goes with it (`AWS` for ECR, `oauth2accesstoken` for Google's, any name for GHCR). The node hands the credentials to the tool that pulls the image and to nothing else: nothing of them is in the sandbox, in its template or in the node's log, and what a registry says when it refuses them comes back without them (`failure_code` `registry_auth_failed`). The sandbox is made from a template of its owner's alone, which a create that names credentials finds and no other: the same image named without them is asked of its registry without a login, whether or not the node has the template. The secret has to exist (404 `secret_not_found`), to be one that may go to the image's registry (its `allowed_hosts` cover the registry's host as the reference writes it, without its port, `docker.io` where it names none; 403 `secret_host_not_allowed` otherwise) and to hold such a pair (400 `invalid_field`), and it goes with `image` (400 `invalid_field` for a sandbox made from none)."
          },
          "snapshot": {
            "type": "string",
            "description": "A snapshot's id or name: the sandbox goes on from the instant the snapshot was taken, processes included."
          },
          "template": {
            "type": "string",
            "description": "A template's key on the node (`base`, `img-…`), as `GET /internal/node` lists them. Not in the reference README; the benchmark scripts use it."
          },
          "limits": {
            "$ref": "#/components/schemas/SandboxLimitsCreate"
          },
          "name": {
            "$ref": "#/components/schemas/Name"
          },
          "env": {
            "$ref": "#/components/schemas/Env",
            "description": "Added to the sandbox's `env` for this command; the same name wins here."
          },
          "metadata": {
            "$ref": "#/components/schemas/Metadata"
          },
          "volumes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/VolumeMountCreate"
            },
            "description": "Volumes to mount before the call returns. At most two that are not shared."
          },
          "public_ports": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Port"
            },
            "maxItems": 64,
            "description": "Ports anybody may open at their short preview URL."
          },
          "public_ports_except": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "$ref": "#/components/schemas/Port"
            },
            "maxItems": 64,
            "description": "Every port but these is public as well: its short preview URL opens it for anybody, as a port in `public_ports` (how E2B makes a sandbox's ports; the control plane's E2B compatibility gives `[49983]`, envd's port, which a sandbox token opens). `[]`: every port. Null (the default): only `public_ports`. Ports are taken once each, in order."
          },
          "inbound": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "$ref": "#/components/schemas/InboundPort"
            },
            "maxItems": 64,
            "description": "Ports open to the internet at the sandbox's own IPv6 address from the start, each with its sources (`…/tcp/{port}` opens one later). A node without a pool of addresses refuses a list that is not empty: 409 `no_ipv6`."
          },
          "sleep_after_seconds": {
            "type": [
              "number",
              "null"
            ],
            "minimum": 1,
            "maximum": 1000000000,
            "default": 30,
            "description": "Sleep after this long idle. Null: never by itself. The default is the node's (`--sleep-after-seconds`, 30 unless set)."
          },
          "expires_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Delete the sandbox at this time, which must be in the future (or what `on_expiry` says). Null or absent: never."
          },
          "on_expiry": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "delete",
              "sleep",
              "hold",
              null
            ],
            "default": "delete",
            "description": "What `expires_at` does when it comes: `delete` the sandbox (null or absent: this); `sleep`, put it to sleep whatever goes on in it, and keep it (its `expires_at` is null from then on, and a request wakes it as ever); `hold`, the same, held asleep (`held_asleep` is set then: a request does not wake it). How E2B pauses a sandbox when its time is up (the control plane's E2B compatibility). Another word is 400 `invalid_field`."
          },
          "held_asleep": {
            "type": [
              "boolean",
              "null"
            ],
            "default": false,
            "description": "True: while it sleeps, a request to one of its ports does not wake it: the preview listener answers 502 `sandbox_paused` in its place, as E2B's proxy answers for a paused sandbox (`x-preview-ingress`), and only a call of the API (or its own timer) wakes it. It is not put to sleep by this: `POST /v1/sandboxes/{id}/sleep` does that. False or null: as any sandbox. (E2B's `pause` without its `autoResume`, which the control plane's E2B compatibility sets, and takes away at its `connect`.)"
          },
          "sleep_notice_ms": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "maximum": 60000,
            "description": "As in `SandboxCreate`: accepted, and of no effect, since the sandbox never sleeps."
          },
          "sleep_hold_max_ms": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "maximum": 900000,
            "description": "As in `SandboxCreate`: accepted, and of no effect, since the sandbox never sleeps."
          },
          "suspend_after_ms": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 1000,
            "maximum": 86400000,
            "description": "As in `SandboxCreate`: accepted, and of no effect, since the sandbox never sleeps."
          },
          "wake_daily_at": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^([01][0-9]|2[0-3]):[0-5][0-9]$",
            "description": "As in `SandboxCreate`: accepted, and of no effect, since the sandbox never sleeps."
          },
          "checkpoint": {
            "$ref": "#/components/schemas/CheckpointSetting",
            "description": "As in `SandboxCreate`: accepted, and its checkpoints taken while the command runs."
          },
          "delete_after_idle_ms": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int64",
            "minimum": 1000,
            "description": "As in `SandboxCreate`: accepted, and of no effect, since the sandbox never sleeps."
          },
          "hostname": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 63,
            "pattern": "^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$",
            "description": "As in `SandboxCreate`."
          },
          "stop": {
            "$ref": "#/components/schemas/SandboxStop"
          },
          "secret_env": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "maxProperties": 64,
            "description": "As in `SandboxCreate`: more variables, whose values are secrets, the sandbox's and so the command's; none of them in `env` too (400 `invalid_field`, `param` `secret_env.<name>`). No answer shows a value."
          },
          "command": {
            "description": "A string is one line for `sh -c`; a list is run as it is, without a shell, its first word found as a shell finds it: along the `PATH` of the command's own environment (the image's, with what `env` says). At most 1000 arguments, no NUL bytes.",
            "oneOf": [
              {
                "type": "string",
                "minLength": 1
              },
              {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "minItems": 1,
                "maxItems": 1000
              }
            ]
          },
          "cwd": {
            "type": "string",
            "description": "An absolute path to run in. Default: the image's working directory. A directory that does not exist is not an error of the call: the command ends with exit code 126."
          },
          "user": {
            "type": "string",
            "minLength": 1,
            "maxLength": 64,
            "description": "A user name, a uid, or `uid:gid`. Default: the image's user."
          },
          "timeout_seconds": {
            "type": "number",
            "exclusiveMinimum": 0,
            "maximum": 1000000000,
            "default": 300,
            "description": "As in `RunRequest`, with a default: nothing else would stop the sandbox."
          },
          "stdin": {
            "type": "string",
            "description": "Fed to the command's input, which is then closed (unless `stdin_open`). Base64 when `encoding` says so. The whole request is 64 MiB at the most (413 `body_too_large` beyond): a longer input goes in `…/exec/{exec_id}/stdin` calls, or in a file first."
          },
          "encoding": {
            "type": "string",
            "enum": [
              "utf8",
              "base64"
            ],
            "default": "utf8",
            "description": "`base64`: `stdin` is base64, and `stdout` and `stderr` come back as base64 (in a stream: events carry `base64` instead of `text`). For output that is not text."
          },
          "stream": {
            "type": "boolean",
            "description": "Answer with an event stream. Default: true if the request's `Accept` contains `text/event-stream`."
          },
          "wait_for_reader": {
            "type": "boolean",
            "default": false,
            "description": "Have the command wait for whoever reads this call's answer, instead of running ahead of it. As in `RunRequest`."
          },
          "region": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "na",
              "eu",
              "ap",
              null
            ],
            "description": "The region to make it in, read by the API and not passed on. Null or left out: the region the API itself runs in. Where no machine of that region can take it, the next region does; what has to go to one machine (made from a snapshot or with volumes, or sent again with the same `Idempotency-Key`) goes there, whatever the region. A volume made from a backup is made in the backup's region: another is 400 `invalid_field`. Not a region: 400 `unknown_region`. The answer's `region` says where it was made."
          }
        },
        "additionalProperties": false
      },
      "NodeReady": {
        "description": "When the machine of the sandbox's template counts as up: the node saves the template, which every sandbox of these settings is cloned from, only once this check passes, so that the service is running in each of them from the start. One of three. `tcp` and `http` are tried by the node from the host, at the address the preview URLs reach the sandbox by: nothing runs in the sandbox for them, so an image without a shell can be checked, and the service has to listen on all addresses, not on 127.0.0.1 alone. `command` runs in the sandbox as the image's user, by `sh`, like the label `sbx.ready`. Tried every 0.2 s for at most 120 s; a start command that ends with a failure meanwhile ends the wait at once. Either way the sandbox fails with `failure_code` `boot_failed`, and `failure` says what the check last got and what the start command printed last. Needs `start: true` or `start_command` (400 `invalid_start`). Not given: the template is saved without waiting.",
        "oneOf": [
          {
            "type": "object",
            "required": [
              "tcp",
              "port"
            ],
            "properties": {
              "tcp": {
                "const": true,
                "description": "The port takes a connection."
              },
              "port": {
                "$ref": "#/components/schemas/Port"
              }
            },
            "additionalProperties": false
          },
          {
            "type": "object",
            "required": [
              "http",
              "port"
            ],
            "properties": {
              "http": {
                "type": "string",
                "pattern": "^/[!-~]*$",
                "maxLength": 2048,
                "description": "A path, with a query if it has one, in printable ASCII without spaces: `GET` of it is answered with a status from 200 to 399. The request carries `Host: localhost:<port>` and `Connection: close`; a connection is waited for 2 s, the answer 5 s."
              },
              "port": {
                "$ref": "#/components/schemas/Port"
              }
            },
            "additionalProperties": false
          },
          {
            "type": "object",
            "required": [
              "command"
            ],
            "properties": {
              "command": {
                "type": "string",
                "minLength": 1,
                "maxLength": 65536,
                "description": "A shell command that succeeds once the start command is up. No NUL bytes."
              }
            },
            "additionalProperties": false
          }
        ]
      },
      "Port": {
        "type": "integer",
        "minimum": 1,
        "maximum": 65535,
        "description": "A TCP port."
      },
      "NodeEgressRules": {
        "type": [
          "object",
          "null"
        ],
        "description": "Egress rules as a request gives them (see `Egress`). Every key may be left out. At creation a key left out is `allow` for `default` and empty for a list. In a PATCH the object is merged into the sandbox's rules: a key that is given replaces what was there (a list is replaced whole), a key given as null goes back to nothing, a key left out stays. Null for the whole field takes every rule away. An `allow` list under `default: allow` would do nothing and is refused, and so is an `allow` entry that lies wholly within the ranges no sandbox reaches. Each list takes at most 1000 entries, `http` and `inject` at most 100 hosts each.",
        "properties": {
          "default": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "allow",
              "deny",
              null
            ]
          },
          "allow": {
            "type": [
              "array",
              "null"
            ],
            "maxItems": 1000,
            "items": {
              "$ref": "#/components/schemas/EgressEntry"
            }
          },
          "deny": {
            "type": [
              "array",
              "null"
            ],
            "maxItems": 1000,
            "items": {
              "$ref": "#/components/schemas/EgressEntry"
            }
          },
          "http": {
            "type": [
              "array",
              "null"
            ],
            "maxItems": 100,
            "items": {
              "$ref": "#/components/schemas/EgressHttp"
            }
          },
          "inject": {
            "type": [
              "array",
              "null"
            ],
            "maxItems": 100,
            "items": {
              "$ref": "#/components/schemas/NodeEgressInject"
            }
          },
          "deny_ports": {
            "type": [
              "array",
              "null"
            ],
            "maxItems": 64,
            "items": {
              "type": "integer",
              "minimum": 1,
              "maximum": 65535
            },
            "description": "Ports no connection reaches, TCP or UDP, whatever else the rules say: at most 64, each from 1 to 65535. Null or empty: none."
          }
        },
        "additionalProperties": false
      },
      "NodeEgressInject": {
        "type": "object",
        "description": "Headers for one host. For a TLS connection to the host on port 443 the node ends the sandbox's TLS itself, under a certificate of its own authority (which a sandbox with `inject` rules is made to trust: its certificate is in the system's store and at `/etc/sbx/egress-ca.pem`), adds the headers to each HTTP/1.1 request, in place of any the sandbox sent under those names, and sends the request to the host over a TLS connection of its own, after the host has shown a certificate for its name that the web's authorities vouch for. The request's Host header has to be the host, and its target a path. A request that is not inside TLS is passed on as it is, without the headers. `TRACE` and `CONNECT` are refused. Requests on such a connection are read as strictly as those to a host in `http` (see `EgressHttp`): HTTP/1.1 only, and no upgrade but a WebSocket's, asked for by the first request of a connection. The node's own answers on such a connection are in `x-egress-proxy`.",
        "required": [
          "host",
          "headers"
        ],
        "properties": {
          "host": {
            "type": "string",
            "maxLength": 300,
            "description": "A host name, or `*.` and a name. Not an address."
          },
          "headers": {
            "type": "object",
            "minProperties": 1,
            "maxProperties": 20,
            "description": "Each header's name and value. In a value, `${secrets.NAME}` stands for the secret of that name (`PUT /v1/secrets/{name}`), which has to be one that may go to `host` (its `allowed_hosts`; 403 `secret_host_not_allowed` otherwise); the sandbox object shows the reference, never the secret. Headers that say where a request goes or where it ends (`Host`, `Content-Length`, `Transfer-Encoding`, `Connection`, `Upgrade`, `TE`, `Trailer`, `Keep-Alive`, `Expect`) cannot be set.",
            "additionalProperties": {
              "type": "string",
              "maxLength": 8192
            }
          }
        },
        "additionalProperties": false
      },
      "NodeRuntime": {
        "type": "object",
        "description": "Runtime settings (src/runtime.rs): the guest's PID 1 applies the sysctls, the tmpfs mounts, /dev/shm's size and the resolver as the machine boots, before its template is saved, and the limits in the start command's process and in every command's (as `docker exec`). Each part is held to what the guest takes; one it does not is refused naming it (`invalid_field`, `unknown_field`), and a setting the guest's kernel refuses (a sysctl it does not have) fails the build (`boot_failed`, saying which). The guest's file is versioned (`sbx-runtime 1`): a guest that does not know what it is asked for does not start the service without it. Without any: open files 1024, raised to 524288 at the most, as under Docker; /dev/shm half of the memory; the image's resolver, or 1.1.1.1 and 8.8.8.8.",
        "additionalProperties": false,
        "properties": {
          "ulimits": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": {
              "anyOf": [
                {
                  "type": "integer",
                  "minimum": -1
                },
                {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "soft",
                    "hard"
                  ],
                  "properties": {
                    "soft": {
                      "type": "integer",
                      "minimum": -1
                    },
                    "hard": {
                      "type": "integer",
                      "minimum": -1
                    }
                  }
                }
              ]
            },
            "description": "By Docker's names (core, cpu, data, fsize, locks, memlock, msgqueue, nice, nofile, nproc, rss, rtprio, rttime, sigpending, stack): a number for both limits, or `{soft, hard}`, the soft no higher than the hard; -1 is none. `nofile` from 1 to 1048576 (the guest's `fs.nr_open`), never -1. Shown as `{soft, hard}`."
          },
          "sysctls": {
            "type": [
              "object",
              "null"
            ],
            "maxProperties": 64,
            "additionalProperties": {
              "type": [
                "string",
                "integer"
              ]
            },
            "description": "Those Docker lets a container set, by name: `net.*`, `kernel.shm*`, `kernel.msg*`, `kernel.sem`, `fs.mqueue.*`; a value of 1 to 1024 bytes on one line. Shown as strings."
          },
          "tmpfs": {
            "type": [
              "array",
              "null"
            ],
            "maxItems": 16,
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "path"
              ],
              "properties": {
                "path": {
                  "type": "string",
                  "maxLength": 1024,
                  "description": "From the root; not /proc, /sys, /dev, /run, /sbx, /etc/sbx, /run/sbx or under them (/run is a tmpfs already; /dev/shm's size is `shm_size_bytes`)."
                },
                "size_bytes": {
                  "type": [
                    "integer",
                    "null"
                  ],
                  "minimum": 1048576,
                  "maximum": 1099511627776,
                  "description": "`null`: half of the memory."
                },
                "mode": {
                  "type": [
                    "integer",
                    "null"
                  ],
                  "minimum": 0,
                  "maximum": 4095,
                  "description": "`null`: 0o1777."
                }
              }
            },
            "description": "Mounted with set-user bits and devices off."
          },
          "dns": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": false,
            "properties": {
              "servers": {
                "type": [
                  "array",
                  "null"
                ],
                "maxItems": 3,
                "items": {
                  "type": "string"
                }
              },
              "search": {
                "type": [
                  "array",
                  "null"
                ],
                "maxItems": 6,
                "items": {
                  "type": "string",
                  "maxLength": 253
                }
              },
              "options": {
                "type": [
                  "array",
                  "null"
                ],
                "maxItems": 16,
                "items": {
                  "type": "string",
                  "maxLength": 64
                }
              }
            },
            "description": "/etc/resolv.conf in the place of the image's. Where the node answers a sandbox's questions itself (on its project's network; egress rules that can refuse a name), it answers in the place of the servers named: they are asked directly only by a sandbox it does not answer for."
          },
          "shm_size_bytes": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 1048576,
            "maximum": 1099511627776
          }
        }
      },
      "SandboxLimitsCreate": {
        "type": "object",
        "description": "The shape a create asks for. Each part that is not given (or is null) is the node's base template's, which is what a sandbox that asks for none gets. Up to the most this node gives a sandbox, `max_vcpus` and `max_memory_bytes` of `GET /internal/node` (by default the host's CPUs, up to 32, and its memory; never less than the base template's shape); more is 400 `invalid_field`. Each shape of an image is a template of its own: the first sandbox of a shape the node has not built that image in is returned `starting` while the node builds it, from the same image, and later ones are cloned from it. Without `image`, another shape than the base template's is built from the node's base image. A sandbox made from a `snapshot` or a `template` has that one's shape: asking for it is fine, for another 400 `invalid_field` (param `limits`). The disk's size is the node's: `disk_bytes` is 400 `invalid_field`.",
        "properties": {
          "vcpus": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 1,
            "maximum": 32,
            "description": "Virtual CPUs."
          },
          "memory_bytes": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int64",
            "minimum": 134217728,
            "multipleOf": 2097152,
            "description": "Memory, in bytes: an even number of MiB (a whole number of 2 MiB pages), 128 MiB or more. Any other size is 400 `invalid_field` (param `limits.memory_bytes`), as is the same size in a template made ahead (`POST /internal/templates`) and a shape of the ready list (`ready_images.unread_lines`): a node that keeps its templates' memory in huge pages loads a machine's memory in 2 MiB pages, and Firecracker refuses to load one of another size, so a template of 285 MiB would be built and every sandbox made from it fail. Not rounded: a sandbox has the size it is asked for, or is refused."
          }
        },
        "additionalProperties": false
      },
      "Name": {
        "type": "string",
        "pattern": "^[a-z0-9]([a-z0-9\\-]{0,61}[a-z0-9])?$",
        "description": "A name given by the caller to a sandbox, a snapshot, a volume or an SSH key: 1 to 63 lower-case letters, digits and hyphens, not starting or ending with a hyphen, and not starting with an id prefix (`sbx-`, `exe-`, `snap-`, `vol-`, `evt-`, `req-`; an SSH key's not with `sshkey-` either). A DNS label that cannot be taken for an id. Unique among sandboxes, among snapshots, among volumes and among SSH keys."
      },
      "Env": {
        "type": "object",
        "description": "Environment variables. Names match `[A-Za-z_][A-Za-z0-9_]*`; values hold no NUL byte; at most 256 KiB in all.",
        "propertyNames": {
          "pattern": "^[A-Za-z_][A-Za-z0-9_]*$"
        },
        "additionalProperties": {
          "type": "string"
        }
      },
      "Metadata": {
        "type": "object",
        "description": "Free key-value pairs for the caller's own use; lists of sandboxes filter by them. At most 64 keys of 1 to 64 characters, values of up to 1024. Not a place for secrets: metadata are stored in clear, and every set of them a sandbox has had while it used something is kept with its usage for thirteen months, also after the sandbox is deleted (`GET /v1/usage` groups and filters by them).",
        "maxProperties": 64,
        "propertyNames": {
          "minLength": 1,
          "maxLength": 64
        },
        "additionalProperties": {
          "type": "string",
          "maxLength": 1024
        }
      },
      "VolumeMountCreate": {
        "type": "object",
        "description": "A volume to mount in a sandbox that is being made. Unknown keys are refused.",
        "required": [
          "path",
          "volume"
        ],
        "properties": {
          "path": {
            "type": "string",
            "description": "Where the volume is mounted in the sandbox: an absolute path other than `/`."
          },
          "volume": {
            "type": "string",
            "description": "The volume's id or name."
          },
          "read_only": {
            "type": [
              "boolean",
              "null"
            ],
            "default": false,
            "description": "Mount it read-only. For a shared volume this is enforced on the node, not in the sandbox."
          },
          "copy": {
            "type": [
              "boolean",
              "null"
            ],
            "default": false,
            "description": "`true`: a volume that is new (its file system holds nothing yet) is given what the sandbox's image has at `path` when it is first mounted, as Docker gives a new named volume: every file, directory and link, with its owner, mode, times, extended attributes and hard links (a database image's data directory, with the owner its server wants); and it is left empty then, without `lost+found`, as a new named volume is (a server that sets up its data directory refuses one that holds anything). A volume that holds something is mounted as it is. Only when a sandbox is made: a volume attached later (`PATCH`) takes no `copy` (400 `unknown_field`)."
          },
          "version": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/DriveVersion"
              },
              {
                "type": "null"
              }
            ],
            "description": "A version of the Drive `volume` names, as a snapshot of it says it (slice D7): the sandbox is given a read-only view of that version, its own, fetched from the store as it is read, whatever has the Drive and wherever it is (the Drive may be deleted since). Mounted read-only."
          }
        },
        "additionalProperties": false
      },
      "DriveVersion": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "name",
          "bytes",
          "epoch",
          "number",
          "token",
          "refs",
          "store",
          "key"
        ],
        "description": "A version of a Drive in the object store (slice D4, a root of its table of versions), as a snapshot of it says it (slice D7): what a fork, a restore and a read-only view are made of.",
        "properties": {
          "name": {
            "type": "string",
            "pattern": "^disks/vol-[0-9a-hjkmnp-tv-z]{16}/e[0-9]+/r[0-9]{10}-[0-9a-f]+$",
            "description": "Its root's name in the store."
          },
          "bytes": {
            "type": "integer",
            "format": "int64",
            "minimum": 0
          },
          "epoch": {
            "type": "integer",
            "format": "int64",
            "minimum": 1
          },
          "number": {
            "type": "integer",
            "format": "int64",
            "minimum": 1
          },
          "token": {
            "type": "string",
            "pattern": "^[0-9a-f]+$"
          },
          "refs": {
            "type": "string",
            "description": "Its list of references, in the store: what the control plane's collector keeps for it."
          },
          "store": {
            "type": "string",
            "description": "The identity of the store its root, pages and frames are in."
          },
          "key": {
            "type": "string",
            "description": "The number of the key it is sealed with (empty where a version from before keys were numbered)."
          }
        }
      },
      "InboundPort": {
        "type": "object",
        "description": "A port open to the internet at the sandbox's own IPv6 address, to the sources it lists and nobody else.",
        "required": [
          "protocol",
          "port",
          "from"
        ],
        "properties": {
          "protocol": {
            "type": "string",
            "enum": [
              "tcp",
              "udp"
            ]
          },
          "port": {
            "type": "integer",
            "minimum": 1,
            "maximum": 65535
          },
          "from": {
            "type": "array",
            "minItems": 1,
            "maxItems": 32,
            "items": {
              "type": "string"
            },
            "description": "The IPv6 prefixes it is open to (`2001:db8::/32`; a single address as the prefix of 128 bits it is, `2001:db8::7/128`; `::/0` for everybody), each once, in their order."
          }
        }
      },
      "CheckpointSetting": {
        "oneOf": [
          {
            "type": "object",
            "properties": {
              "every_seconds": {
                "type": [
                  "integer",
                  "null"
                ],
                "minimum": 1,
                "maximum": 600,
                "description": "How often, in seconds. Left out or null: 10."
              }
            },
            "additionalProperties": false
          },
          {
            "type": "null"
          }
        ],
        "description": "Checkpoints while the sandbox runs: every `every_seconds` its memory and its disk as they are at one instant go to the node's object store (the node pauses its machine for a few milliseconds for it; what it wrote since the last one is what goes), so that when the node's host is lost the sandbox is taken over elsewhere with its processes, from its newest checkpoint, losing at most what it did since. A checkpoint is begun only once the one before is in the store: a sandbox that writes more than the node's link for them takes falls behind (`checkpoint.behind_seconds`) rather than filling it. Asleep, its sleep's image is what it is taken over from. Null (the default): none. Any sandbox may have it, given when it is made or by a `PATCH` (the control plane also gives it to the keyed instances of an app whose `checkpoint` says so); what the store keeps of its newest checkpoint is billed as a sleeping sandbox's state while it runs (`checkpoint.stored_bytes`). A node that does not copy disks to an object store (`--upload`), or whose memory is not in layers, refuses it (409 `checkpoints_unavailable`, `param` `checkpoint`), and a control plane makes a sandbox with it only on a node that takes checkpoints. Its Drives are pinned with its own disk at the same instant, and a takeover from it brings them back as they were then, with its memory; none is taken while a volume that is not a Drive is attached (such a volume has no copy in the store), or while a read of its guest is held up on a drive of its machine. A checkpoint does not reset the guest's vsock connections (the agent's calls go on), and the first one after a wake from a sleep whose image the store has sends only what the guest wrote since."
      },
      "SandboxStop": {
        "type": [
          "object",
          "null"
        ],
        "description": "How the sandbox's start command is stopped before its machine is ended for good: before a restart (`POST /v1/sandboxes/{id}/restart`) and a delete. The signal goes to the start command, which is then given `grace_ms` to end; what has not ended then goes with the machine. A machine that sleeps or is paused is not stopped: it goes on from where it was. Not given: the start command goes with its machine, as it always has. Unknown keys: 400 `unknown_field`; a signal that is none of these, or a grace beyond ten minutes: 400 `invalid_field`.",
        "properties": {
          "signal": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "SIGTERM",
              "SIGINT",
              "SIGQUIT",
              "SIGHUP",
              "SIGUSR1",
              "SIGUSR2",
              "SIGKILL",
              "SIGWINCH",
              "SIGPWR",
              "SIGSTOP",
              "TERM",
              "INT",
              "QUIT",
              "HUP",
              "USR1",
              "USR2",
              "KILL",
              "WINCH",
              "PWR",
              "STOP",
              null
            ],
            "description": "By its name, with `SIG` or without. Null: the image's own (its `STOPSIGNAL`), else SIGTERM."
          },
          "grace_ms": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "maximum": 600000,
            "description": "Null or not given: 10000."
          }
        },
        "additionalProperties": false
      },
      "ExecResult": {
        "description": "A command with the end of its output. With `status` `running` (in a 202, or a `GET` whose `wait_seconds` ran out) the output is what there is so far; any other status comes with all of it.",
        "allOf": [
          {
            "$ref": "#/components/schemas/Exec"
          },
          {
            "type": "object",
            "required": [
              "stdout",
              "stderr",
              "truncated",
              "encoding"
            ],
            "properties": {
              "stdout": {
                "type": "string",
                "description": "The last 1 MiB of stdout. Text, with bytes that are not UTF-8 replaced; base64 when `encoding` is `base64`."
              },
              "stderr": {
                "type": "string",
                "description": "The last 1 MiB of stderr."
              },
              "truncated": {
                "type": "boolean",
                "description": "True when either stream was longer than 1 MiB, or part of the output was no longer kept."
              },
              "encoding": {
                "type": "string",
                "x-extensible-enum": [
                  "utf8",
                  "base64"
                ],
                "description": "How `stdout` and `stderr` are written: as the request asked."
              }
            }
          }
        ]
      },
      "Exec": {
        "type": "object",
        "description": "A command: its handle and how it stands.",
        "required": [
          "id",
          "object",
          "sandbox_id",
          "command",
          "tty",
          "pid",
          "status",
          "exit_code",
          "signal",
          "started_at",
          "duration_seconds",
          "usage",
          "output"
        ],
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^exe-[0-9a-hjkmnp-tv-z]{16}$",
            "description": "`exe-` and 16 characters. With an `Idempotency-Key` it is derived from the sandbox's id, the call (`run` or `exec`) and the key."
          },
          "object": {
            "type": "string",
            "const": "exec"
          },
          "sandbox_id": {
            "type": "string",
            "pattern": "^sbx-[0-9a-hjkmnp-tv-z]{16}$"
          },
          "command": {
            "type": "string",
            "description": "The command as text: the line, or the arguments joined by spaces; `shell` for a terminal started without a command. At most 1024 characters. A command that came with a snapshot or a fork is listed as it was where it started: the sandbox keeps this itself."
          },
          "tty": {
            "type": "boolean",
            "description": "Whether it runs on a terminal."
          },
          "status": {
            "type": "string",
            "x-extensible-enum": [
              "running",
              "exited",
              "timed_out",
              "killed"
            ],
            "description": "`exited`: it ended by itself, with any exit code. `timed_out`: `timeout_seconds` was up. `killed`: a signal ended it."
          },
          "exit_code": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Null while running. 128 plus the signal's number when a signal ended it. A non-zero code is a result, not an error."
          },
          "signal": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^SIG[A-Z0-9]+$",
            "description": "The signal that ended it, as `SIGTERM`, `SIGKILL`, … or `SIG<number>` for one the daemon has no name for; null otherwise."
          },
          "started_at": {
            "type": "string",
            "format": "date-time",
            "description": "From the sandbox's clock: the same in the full object and in a list."
          },
          "pid": {
            "type": "integer",
            "description": "Its process id in the sandbox."
          },
          "duration_seconds": {
            "type": [
              "number",
              "null"
            ],
            "minimum": 0,
            "description": "Wall time from start to end. Null while running."
          },
          "usage": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/ExecUsage"
              },
              {
                "type": "null"
              }
            ],
            "description": "Null while running."
          },
          "output": {
            "$ref": "#/components/schemas/ExecOutput"
          }
        }
      },
      "ExecUsage": {
        "type": "object",
        "required": [
          "cpu_seconds",
          "max_memory_bytes"
        ],
        "properties": {
          "cpu_seconds": {
            "type": "number",
            "minimum": 0,
            "description": "User and system CPU time of the command and the children it waited for."
          },
          "max_memory_bytes": {
            "type": "integer",
            "format": "int64",
            "minimum": 0,
            "description": "The largest resident set of any of them."
          }
        }
      },
      "ExecOutput": {
        "type": "object",
        "description": "How much the command has written and which part of it can still be read.",
        "required": [
          "stdout_bytes",
          "stderr_bytes",
          "start",
          "end"
        ],
        "properties": {
          "stdout_bytes": {
            "type": "integer",
            "format": "int64",
            "minimum": 0,
            "description": "Bytes written to stdout so far."
          },
          "stderr_bytes": {
            "type": "integer",
            "format": "int64",
            "minimum": 0
          },
          "start": {
            "type": "integer",
            "format": "int64",
            "minimum": 0,
            "description": "The position of the oldest byte still kept. Positions count the bytes of both streams together, in the order they were written; the sandbox keeps the last 16 MiB."
          },
          "end": {
            "type": "integer",
            "format": "int64",
            "minimum": 0,
            "description": "The position after the newest byte."
          }
        }
      },
      "NodeErrorResponse": {
        "type": "object",
        "description": "The one shape every error of the API has. Next to `error`, the refusals of a move say a little more: `to` and `epoch` with `moved`, `epoch` with `wrong_epoch`, `disk_ms`, `memory_ms`, `drives_ms` and `image_ms` with `copy_behind`, `image` with `image_missing`, `build` with `build_missing`; and `secret_host_not_allowed` says `allowed_hosts`.",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "$ref": "#/components/schemas/Error"
          },
          "allowed_hosts": {
            "$ref": "#/components/schemas/SecretHosts",
            "description": "With `secret_host_not_allowed`: the hosts the secret may go to, as the secret has them; null where it was given none."
          },
          "to": {
            "type": [
              "string",
              "null"
            ],
            "description": "With `moved` and `sandbox_moved`: the node the sandbox was released to, as the control plane named it, or null."
          },
          "epoch": {
            "type": "integer",
            "format": "int64",
            "minimum": 1,
            "description": "With `moved` and `sandbox_moved`: the epoch the sandbox was released at. With `wrong_epoch`: the epoch this node holds it at, or released it at."
          },
          "offset": {
            "type": "integer",
            "format": "int64",
            "minimum": 0,
            "description": "With `sandbox_moved`: the position in the command's output the call, stream or terminal had reached (a stream's last event's `id`): read on from it where the sandbox went (`Last-Event-ID`, or `from`)."
          },
          "exec_id": {
            "type": "string",
            "description": "With `sandbox_moved` of `POST …/run`: the id of the command the call started and waited for, which goes on where the sandbox went: wait for it there through the control plane with `GET …/exec/{exec_id}?wait_seconds=` once the move is over. The same call made again starts the command a second time, unless it comes with the same Idempotency-Key."
          },
          "disk_ms": {
            "type": [
              "number",
              "null"
            ],
            "minimum": 0,
            "description": "With `copy_behind`: how long the release waited for the store to hold the disk as of the sleep; null where it did."
          },
          "memory_ms": {
            "type": [
              "number",
              "null"
            ],
            "minimum": 0,
            "description": "With `copy_behind`: how long the release waited for the store to hold the memory image of that sleep; null where it did."
          },
          "drives_ms": {
            "type": [
              "number",
              "null"
            ],
            "minimum": 0,
            "description": "With `copy_behind`: how long the release waited for the store to hold the sandbox's Drives as they are (slice D2); null where it did, or where it has none."
          },
          "image_ms": {
            "type": [
              "number",
              "null"
            ],
            "minimum": 0,
            "description": "With `copy_behind`: how long the release waited for the store to hold the image the sandbox's disk is over (its node copies a disk before its template's image is there); null where it did."
          },
          "image": {
            "type": "string",
            "pattern": "^[A-Za-z0-9_-]{1,128}$",
            "description": "With `image_missing`: the id of the image the object store does not have, as the disks over it and a template's `disk_image` name it."
          },
          "build": {
            "type": "string",
            "pattern": "^[0-9a-f]{16}$",
            "description": "With `build_missing`: the build of Firecracker that wrote the sandbox's memory, which this node does not keep."
          }
        }
      },
      "Error": {
        "type": "object",
        "required": [
          "type",
          "code",
          "message",
          "param",
          "retryable",
          "request_id",
          "docs_url"
        ],
        "properties": {
          "type": {
            "$ref": "#/components/schemas/ErrorType"
          },
          "code": {
            "$ref": "#/components/schemas/ErrorCode"
          },
          "message": {
            "type": "string",
            "description": "For people. English."
          },
          "param": {
            "type": [
              "string",
              "null"
            ],
            "description": "The field or query parameter at fault, when there is one."
          },
          "retryable": {
            "type": "boolean",
            "description": "True when the same request may succeed later without anything else changing."
          },
          "request_id": {
            "type": "string",
            "pattern": "^[A-Za-z0-9._\\-]{1,64}$",
            "description": "The `X-Request-Id` of the response it is in, also when that response is a replay for an `Idempotency-Key`."
          },
          "docs_url": {
            "type": [
              "string",
              "null"
            ],
            "description": "The node's `--docs-url` with the code appended, or null."
          }
        }
      },
      "ErrorType": {
        "type": "string",
        "x-extensible-enum": [
          "invalid_request",
          "unauthenticated",
          "forbidden",
          "not_found",
          "conflict",
          "too_large",
          "internal",
          "bad_gateway",
          "unavailable",
          "timeout"
        ],
        "description": "The kind of failure. It decides the status: `invalid_request` 400, `unauthenticated` 401, `forbidden` 403, `not_found` 404, `conflict` 409, `too_large` 413, `internal` 500, `bad_gateway` 502 (the preview listener, the egress proxy, and a takeover that could not bring its sandbox), `unavailable` 503, `timeout` 504."
      },
      "ErrorCode": {
        "type": "string",
        "x-extensible-enum": [
          "malformed_request",
          "body_too_large",
          "body_memory_full",
          "invalid_json",
          "unknown_field",
          "unknown_parameter",
          "invalid_field",
          "missing_field",
          "invalid_name",
          "invalid_account",
          "invalid_env",
          "invalid_metadata",
          "expires_in_the_past",
          "invalid_egress",
          "invalid_secret",
          "invalid_idempotency_key",
          "invalid_cursor",
          "invalid_start",
          "ambiguous_source",
          "invalid",
          "invalid_image",
          "missing_command",
          "no_such_user",
          "unknown_signal",
          "terminal_needs_exec",
          "exec_does_not_wait",
          "websocket_expected",
          "invalid_path",
          "name_too_long",
          "missing_path",
          "moved_into_itself",
          "invalid_public_key",
          "invalid_owner",
          "invalid_time",
          "clock_mismatch",
          "invalid_egress_mark",
          "missing_api_key",
          "invalid_api_key",
          "invalid_token",
          "needs_api_key",
          "needs_operator_key",
          "needs_control_key",
          "spend_limit_reached",
          "plan_cap_reached",
          "plan_limit_reached",
          "token_scope",
          "permission_denied",
          "needs_gateway_key",
          "ssh_key_not_authorized",
          "secret_host_not_allowed",
          "no_such_route",
          "sandbox_not_found",
          "snapshot_not_found",
          "volume_not_found",
          "backup_not_found",
          "backup_not_ready",
          "backup_unusable",
          "backup_exists",
          "volume_restoring",
          "template_not_found",
          "secret_not_found",
          "exec_not_found",
          "file_not_found",
          "ssh_key_not_found",
          "not_found",
          "conflict",
          "name_taken",
          "volume_in_use",
          "volume_exists",
          "volume_had",
          "too_many_revoked_tokens",
          "mount_in_use",
          "detached_mount_in_use",
          "duplicate_volume",
          "mount_path_taken",
          "too_many_volumes",
          "exclusive_volume_attached",
          "snapshot_lost",
          "busy",
          "no_ipv6",
          "checkpoints_unavailable",
          "invalid_from",
          "invalid_port",
          "invalid_inbound",
          "sandbox_failed",
          "command_lost",
          "sandbox_moved",
          "stdin_closed",
          "not_a_terminal",
          "is_a_directory",
          "not_a_directory",
          "directory_not_empty",
          "already_exists",
          "symlink_loop",
          "file_busy",
          "not_a_regular_file",
          "cross_device",
          "read_only",
          "idempotency_key_reused",
          "idempotency_in_progress",
          "base_template",
          "not_a_drive",
          "not_copied",
          "no_version",
          "not_publishable",
          "template_lost",
          "template_unusable",
          "not_loadable",
          "store_differs",
          "fetch_failed",
          "ssh_key_exists",
          "too_many_ssh_keys",
          "needs_forward",
          "too_many_forwards",
          "connect_failed",
          "agent_failed",
          "sandbox_too_old",
          "disk_full",
          "file_changed",
          "internal",
          "no_capacity",
          "memory_full",
          "account_share_reached",
          "drive_full",
          "unavailable",
          "no_preview_domain",
          "not_served_here",
          "caller_gone",
          "image_not_found",
          "image_pull_failed",
          "registry_auth_failed",
          "guest_mitigations_fixed",
          "image_unpack_failed",
          "no_such_preview",
          "private_port",
          "invalid_link",
          "port_not_listening",
          "malformed_answer",
          "e2b_sandbox_not_found",
          "sandbox_paused",
          "moved_under_request",
          "egress_denied",
          "host_mismatch",
          "method_not_allowed",
          "secret_missing",
          "upstream_unreachable",
          "egress_connections_full",
          "fenced",
          "moved",
          "wrong_epoch",
          "copy_behind",
          "volumes_attached",
          "gone",
          "store_unfenced",
          "not_frozen",
          "hold_over",
          "version_failed",
          "takeover_failed",
          "already_here",
          "build_missing",
          "log_not_found",
          "log_anew",
          "key_not_found",
          "key_id_mismatch",
          "drive_unreadable",
          "key_missing",
          "image_missing",
          "image_not_here",
          "log_key_not_found",
          "destination_not_found",
          "log_without_version",
          "app_not_found",
          "needs_edge",
          "app_full_here",
          "app_at_capacity",
          "network_not_found",
          "network_conflict",
          "no_project_address",
          "no_control_plane",
          "upgrade_failed",
          "upgrade_not_here",
          "upgrade_not_needed",
          "upgrade_in_progress",
          "upgrade_not_found",
          "awake",
          "rollback_gone",
          "invalid_proxy",
          "proxy_failed",
          "static_instance",
          "static_not_found",
          "invalid_spec",
          "no_static_dir"
        ],
        "description": "Which failure. The top-level `x-error-codes` table of this document says what each one means."
      },
      "SecretHosts": {
        "type": [
          "array",
          "null"
        ],
        "maxItems": 100,
        "items": {
          "type": "string",
          "minLength": 1,
          "maxLength": 255
        },
        "description": "The hosts a secret may be sent to, and no other: by `egress.inject` to the host of its entry, and as `image_pull_secret` to the registry of the `image` (the registry's host as the reference writes it, without its port; `docker.io` for a reference that names no registry). Each is a host name, or `*.` and a name for every name under it (not the name itself), as `egress.allow` writes names: compared without case and without a trailing dot, kept in lower case, each once. No address and no port: a secret goes where the node has the server prove that it is the host named, and a host's ports are the host's. A rule's host that is a wildcard is covered only by a wildcard as wide or wider. null: the secret was given no hosts (every secret kept before secrets had hosts), and goes to none; an empty list goes to none either."
      },
      "SandboxList": {
        "type": "object",
        "description": "One page of sandboxes, newest first.",
        "required": [
          "data",
          "next_cursor"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Sandbox"
            }
          },
          "next_cursor": {
            "$ref": "#/components/schemas/Cursor"
          }
        }
      },
      "Sandbox": {
        "type": "object",
        "description": "A sandbox: one Firecracker microVM with its disk, its settings and what it has used.",
        "required": [
          "id",
          "object",
          "name",
          "status",
          "failure",
          "failure_code",
          "image",
          "snapshot",
          "metadata",
          "volumes",
          "public_ports",
          "public_ports_except",
          "egress",
          "sleep_after_seconds",
          "expires_at",
          "on_expiry",
          "held_asleep",
          "limits",
          "created_at",
          "booted_at",
          "last_active_at",
          "awake_because",
          "usage",
          "guest_mitigations",
          "tmp",
          "host_failure",
          "sleep_tier",
          "project_address",
          "disk_writes",
          "delete_after_idle_ms",
          "idle_since",
          "region",
          "usable_at",
          "hydrated_at",
          "hydrated_bytes",
          "total_bytes",
          "sleep_notice_ms",
          "sleep_hold_max_ms",
          "suspend_after_ms",
          "wake_daily_at",
          "secret_env",
          "ipv6",
          "inbound",
          "checkpoint",
          "app",
          "awake_ports",
          "connection_idle_seconds",
          "awake_connections",
          "devtools",
          "proxy",
          "start_command",
          "image_digest"
        ],
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^sbx-[0-9a-hjkmnp-tv-z]{16}$",
            "description": "`sbx-` and 16 characters; valid as a DNS label."
          },
          "awake_ports": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Port"
            },
            "description": "The ports whose upgraded connections keep it awake, and by which (with its requests and calls) alone it is idle (`SandboxCreate`); empty for a sandbox that is idle as any is."
          },
          "connection_idle_seconds": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 1,
            "description": "How long a connection on one of `awake_ports` may carry nothing before the node closes it (`SandboxCreate`); null: never for that."
          },
          "awake_connections": {
            "type": "integer",
            "minimum": 0,
            "description": "The connections on its `awake_ports` open now: while there is one, it is awake (`awake_because` says so) and is not put to sleep by the idle rule."
          },
          "start_command": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/SandboxStartCommand"
              },
              {
                "type": "null"
              }
            ],
            "description": "How its start command runs, how often it was started again in its machine after it ended, and how its last run ended, as its guest said last (once a second while its machine runs: src/restarts.rs). The node has the guest start it again in the same machine, with its disk and volumes as they are, for an instance that is its owner's state (a key's, a stateful service's: a database's), once its command is up; an instance of an app's group is retired instead, and another takes its place; any other sandbox's start command is left as it ended. Null where its guest has said nothing of it: it has none, it has not begun (a stateful service's waits for its volumes), or its machine has not run since the node knew to ask."
          },
          "devtools": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": false,
            "required": [
              "port",
              "path"
            ],
            "description": "Of a sandbox of a browser's image (one with the label `sbx.devtools`, the port of its browser's DevTools protocol): the browser's own address, as the node asked it (`/json/version`) when it built the image's template. The browser is the template's, the same in every sandbox made from it, and this goes with the sandbox wherever it goes. A client that takes nothing but a WebSocket's address connects to `path` at the signed link of `port`. Null for any other sandbox.",
            "properties": {
              "port": {
                "type": "integer",
                "minimum": 1,
                "maximum": 65535
              },
              "path": {
                "type": "string",
                "pattern": "^/devtools/browser/[A-Za-z0-9-]{1,64}$",
                "description": "`/devtools/browser/<the browser's id>`."
              }
            }
          },
          "proxy": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/SandboxProxy"
              },
              {
                "type": "null"
              }
            ],
            "description": "Its own proxy (`SandboxCreate`'s `proxy`): its address and the user name it is asked with, never its password. Null where it has none."
          },
          "image_digest": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^[A-Za-z0-9]+:[0-9A-Fa-f]{32,}$",
            "description": "The image its disk was made of, by the digest of its manifest (or list of manifests) as its registry named it when its template was built (`Template.image_digest`): what a tag named then. A tag that is moved since makes another image of the sandboxes made after it (a newer template), and this one's stays. While its template is being built (`starting`): the image the build is of, where the create knew it (a tag found to name a newer image than the template there holds, built by that digest), else null; and null where the template does not say (one built from a disk file, or before templates said so)."
          },
          "app_id": {
            "type": "string",
            "description": "Of an instance of an app of a control plane's (`SandboxCreate.instance_of`: an app of the Sandboxes product, control/src/sandboxes.rs): the app, as the control plane names it, for the sandbox's whole life. Not there for any other sandbox."
          },
          "version_id": {
            "type": "string",
            "description": "With `app_id`: the version of the app its create asked for, as the control plane names it. Through a control plane, the version of the image it was made of (`image_digest`): a tag moved under the app is a version of its own."
          },
          "key": {
            "type": "string",
            "description": "With `app_id`: what it is found by among the app's instances, its name, or its id where it has none."
          },
          "app": {
            "type": [
              "string",
              "null"
            ],
            "description": "For an instance of an app, one of those the node makes and deletes by the app's requests (`PUT /internal/apps/{key}`): the app, by the key of its template, as the node's inventory says it. Null for any other sandbox, a keyed instance of an app among them (its `metadata` say its app and its key). A control plane shown an instance first in the answer to a call keeps it out of its edges' table by this, as it keeps one its node's inventory lists."
          },
          "ipv6": {
            "type": [
              "string",
              "null"
            ],
            "description": "Its own public IPv6 address, for its whole life: what it sends over IPv6 leaves from it, untranslated, and what comes to it is let in as `inbound` says. Given when it is made, on a node with a pool of addresses; kept through sleep, snapshot and move; a fork, or a sandbox made from a snapshot, has one of its own. Null: IPv4 alone (its node has no pool, or its template's agent is from before addresses of their own)."
          },
          "inbound": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/InboundPort"
            },
            "description": "The ports open to the internet at `ipv6`, each with the sources it is open to, in the order of protocol and port. Closed by default: empty."
          },
          "checkpoint": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/CheckpointStatus"
              },
              {
                "type": "null"
              }
            ],
            "description": "Its checkpoints while it runs (`SandboxCreate`'s `checkpoint`), and how they go; null where it has none."
          },
          "secret_env": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "The names of the variables of `secret_env` it was made with, in order; never a value. Empty for one made with none."
          },
          "object": {
            "type": "string",
            "const": "sandbox"
          },
          "name": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/Name"
              },
              {
                "type": "null"
              }
            ],
            "description": "The name the caller gave it, or null."
          },
          "status": {
            "type": "string",
            "x-extensible-enum": [
              "starting",
              "running",
              "sleeping",
              "failed"
            ],
            "description": "Information: a caller need not look at it before a call. `starting`: its image is being pulled and built, and calls that need it wait. `sleeping`: memory saved, nothing running (also shown for a sandbox that has only its disk left, and for one that is paused in the node's memory: `sleep_tier` says which); the next call that needs it wakes it. `failed`: it could not be built or woken, or its record on the node could not be read after the node's host went down, and calls that need it answer 409 `sandbox_failed`. The answer of a call that changes it (create, sleep, restart, restore) shows the status that call left."
          },
          "sleep_tier": {
            "oneOf": [
              {
                "type": "string",
                "x-extensible-enum": [
                  "paused",
                  "saved",
                  "disk"
                ]
              },
              {
                "type": "null"
              }
            ],
            "description": "How a `sleeping` sandbox sleeps; null for every other status. `paused`: its machine stands still in the node's memory, with its processes and connections (a node with the pause tier on pauses an idle sandbox instead of saving it), and a call resumes it in about a millisecond; the memory its machine holds is metered as a running sandbox's is (`usage.memory_bytes`, `memory_gib_seconds`), and its CPU is not. `saved`: its memory and the state of its devices are saved on the node, and nothing runs; a call wakes it from them in a few milliseconds. `disk`: only its disk is left (its machine ended by itself, the node's daemon ended without putting it to sleep, or its memory could not be kept); the next call that needs it boots it from the disk, and what ran is gone. `POST /v1/sandboxes/{id}/sleep` saves a sandbox, paused or running."
          },
          "failure": {
            "type": [
              "string",
              "null"
            ],
            "description": "Why the sandbox is `failed`, in a sentence for people; null otherwise. It names nothing of the node: no path, no line of a tool's log. Of a registry that refused the credentials it was asked with, it has the registry's own words, with the credentials cut out of them."
          },
          "failure_code": {
            "oneOf": [
              {
                "type": "string",
                "x-extensible-enum": [
                  "image_not_found",
                  "image_pull_failed",
                  "registry_auth_failed",
                  "image_unpack_failed",
                  "invalid_image",
                  "boot_failed",
                  "no_capacity",
                  "memory_full",
                  "drive_full",
                  "record_lost",
                  "recovery_failed",
                  "start_failed"
                ]
              },
              {
                "type": "null"
              }
            ],
            "description": "Why the sandbox is `failed`, for programs; null otherwise. `image_not_found`: the image is not in its registry, or is not public (a registry that wants a login says no more than that to a create that names no credentials). `image_pull_failed`: the registry could not be reached or did not hand the image out, or the tool that fetches was stopped at a limit of the node's (`failure` says which). `registry_auth_failed`: the registry refused the credentials the create named (`image_pull_secret`); `failure` has what the registry said. `image_unpack_failed`: the image does not fit the disk, is larger than the node takes (`failure` has the numbers), or cannot be unpacked. `invalid_image`: the registry is at an address on the node or in a private network, or the image cannot be started as asked (no ENTRYPOINT or CMD for `start: true`, labels that ask for what cannot be done). `boot_failed`: the machine made from the image did not start, or its start command did not come up (then `failure` says what its ready check got and what it printed last). `no_capacity`: the node had no room. `memory_full`: the host's memory had no room for the machine its template is built in, or for its own. `drive_full`: the node's data drive filled while the image's template was being built. `record_lost`: the sandbox's record cannot be read; its files are kept on the node, but it cannot be started, only deleted. `recovery_failed`: the sandbox's startup recovery could not finish; its files and recovery intent are kept on the node, and it cannot be started, only deleted. `start_failed`: anything else; the node's log has it."
          },
          "image": {
            "type": [
              "string",
              "null"
            ],
            "description": "The OCI image reference the sandbox goes back to. Null only on a node whose base template was built from a disk file."
          },
          "snapshot": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^snap-[0-9a-hjkmnp-tv-z]{16}$",
            "description": "The id of the snapshot whose state the sandbox last took: the one it was created from or restored to. Null for a sandbox made from an image. Not cleared when that snapshot is deleted, and for a fork it names the fork's own temporary snapshot, which is gone by the time the call returns."
          },
          "env": {
            "$ref": "#/components/schemas/Env",
            "description": "The sandbox's environment variables and their values. Absent (not null) when the caller is a sandbox token with none of the scopes `exec`, `files` and `tty`: the values are often credentials, and a token for the sandbox's links is not one for them."
          },
          "metadata": {
            "$ref": "#/components/schemas/Metadata"
          },
          "volumes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SandboxVolume"
            }
          },
          "public_ports": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Port"
            },
            "description": "Ports whose preview URL needs no signature and no token. Duplicates are kept as given."
          },
          "public_ports_except": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "$ref": "#/components/schemas/Port"
            },
            "description": "Every port but these is public as well, as if it were in `public_ports` (`SandboxCreate`); null where that is not said. A sandbox the control plane's E2B compatibility makes has `[49983]`: its ports are public as E2B's are, envd's port behind the sandbox's token."
          },
          "egress": {
            "$ref": "#/components/schemas/Egress"
          },
          "sleep_after_seconds": {
            "type": [
              "number",
              "null"
            ],
            "minimum": 0,
            "description": "The sandbox sleeps after this long idle. Null: never by itself. At least 1 where a caller set it; an instance of an app has what its app says (`sleep_after_ms` of `PUT /internal/apps/{key}`), which may be less."
          },
          "expires_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the sandbox deletes itself (or what `on_expiry` says). Null: never."
          },
          "on_expiry": {
            "type": "string",
            "enum": [
              "delete",
              "sleep",
              "hold"
            ],
            "description": "What `expires_at` does when it comes: `delete`, the sandbox is deleted (the default); `sleep`, it is put to sleep whatever goes on in it and kept, its `expires_at` null from then on; `hold`, the same, held asleep (`held_asleep` set). The control plane's E2B compatibility gives `sleep` or `hold` to a sandbox whose time being up pauses it."
          },
          "held_asleep": {
            "type": "boolean",
            "description": "Asleep and not woken by requests (E2B's paused sandbox, which the control plane's E2B compatibility holds so between its `pause` and its `connect`): while it sleeps, a request to one of its ports does not wake it, and the preview listener answers 502 `sandbox_paused` in its place (`x-preview-ingress`). A call of the API wakes it as ever, and so does its own timer; a request that comes while it is awake goes on to it."
          },
          "sleep_notice_ms": {
            "type": [
              "integer",
              "null"
            ],
            "description": "How long before the idle rule puts it to sleep the programs connected to its guest's `/run/sbx/awake` are told, in milliseconds (`SandboxCreate`); null: not told."
          },
          "sleep_hold_max_ms": {
            "type": "integer",
            "minimum": 0,
            "description": "How long its guest's holds keep it up at the most, in milliseconds (`SandboxCreate`): its own, or the node's. While held, `awake_because` says so."
          },
          "suspend_after_ms": {
            "type": "integer",
            "minimum": 1000,
            "description": "How long the pause tier holds it paused before it sleeps for real, in milliseconds (`SandboxCreate`): its own, held to the node's `--pause-max-seconds`; the node's where it says none."
          },
          "wake_daily_at": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^([01][0-9]|2[0-3]):[0-5][0-9]$",
            "description": "The time of day, in UTC, at which the node wakes it every day while it rests (`SandboxCreate`); null: none."
          },
          "delete_after_idle_ms": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int64",
            "minimum": 1000,
            "description": "After how long asleep the node deletes the sandbox, in milliseconds, counted from `idle_since`: a sandbox that is woken counts again from its next sleep. Null: never for that (the default); the sandbox stays until it is deleted, or until its `expires_at`. A sandbox that does not sleep by itself (`sleep_after_seconds` null) counts the sleeps it is put to (`POST /v1/sandboxes/{id}/sleep`, a restart of its node). Not while it is frozen (its owner's spend limit): a freeze keeps it with its data, and once let go of, the sleep it is in counts from then."
          },
          "idle_since": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When its current sleep began, while `status` is `sleeping`: when its machine was paused or put to sleep, or found to have ended. One that was running when its node stopped sleeps from the moment its node was started again; one that was frozen, from the moment it was let go of. Null while it is not asleep."
          },
          "limits": {
            "$ref": "#/components/schemas/NodeSandboxLimits"
          },
          "guest_mitigations": {
            "type": "boolean",
            "description": "Whether the guest kernel keeps its mitigations against speculative execution (`guest_mitigations` at creation)."
          },
          "tmp": {
            "type": "string",
            "enum": [
              "disk",
              "memory"
            ],
            "description": "Where its /tmp is (`tmp` at creation): on its disk, or a tmpfs in its guest's memory, which is billed as memory and is gone when the sandbox starts again from its disk. One made from a snapshot has the snapshot's sandbox's."
          },
          "init": {
            "oneOf": [
              {
                "type": "string",
                "enum": [
                  "systemd"
                ]
              },
              {
                "type": "null"
              }
            ],
            "description": "What its PID 1 is (`init` at creation): `systemd`, its image's own systemd (a whole system), or null, the platform's init. One made from a snapshot, or a fork, has the snapshot's sandbox's. A whole system has a machine ID of its own (/etc/machine-id, and D-Bus's copy, /var/lib/dbus/machine-id), given when its machine is made (a clone of a template or a snapshot, a fork, a boot from a snapshot's disk) and kept from then on, through sleeps, wakes, moves and boots from its disk; its journal is filed under it (/var/log/journal/<machine ID>)."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "booted_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the machine last started from nothing: at creation, and after every restart from its disk. A sleep and a wake do not change it."
          },
          "last_active_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "The last activity the node saw: a call, a packet on its link, its guest computing (more than 2% of a core over `sleep_after_seconds`, by the guest's own count where the host saw more than that; by the host's count for a template from before guests said what they computed), noted when the node looks."
          },
          "awake_because": {
            "type": [
              "string",
              "null"
            ],
            "description": "Why a running sandbox has not been put to sleep, in a few words of prose. Null unless `status` is `running`. An instance of an app says what the node gave it: a request in its hand, a call in flight, a request that ended less than its `sleep_after_seconds` ago, or that its app's floor keeps it awake. One that a packet on its project's network woke, and that nothing else has kept awake since, says so and which packet it was: `woken by a packet: TCP from 10.64.0.7:41594 to port 5432 [S] …`. Connections inside its project keep no sandbox awake: what comes over them while it sleeps is the node's hold's (`Node.hold`)."
          },
          "usage": {
            "$ref": "#/components/schemas/SandboxUsage"
          },
          "host_failure": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/SandboxHostFailure"
              },
              {
                "type": "null"
              }
            ],
            "description": "What the sandbox last lost to the host it is on, and why (`cause`): its memory when the host went down hard (power, a kernel that gave up), as the node found it when it came back; or its memory when the node was started again with another Firecracker than had saved it, and the sandbox was then used. Null for a sandbox that lost nothing: one that never went through either, and one that was asleep with everything it needs on the host's disk, which wakes as it always does on a node that is what it was. It stays as it is until the next such loss."
          },
          "project_address": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^10\\.(6[4-9]|[7-9][0-9]|1[01][0-9]|12[0-7])(\\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){2}$",
            "description": "Its address on its project's private network: where the project's other sandboxes on that network reach it, and what they see it as, whichever slot of the node it runs in. Null for a sandbox that is on no project's network. Given by whoever tells the node of the network (`setProjectNetwork`), or taken by a create that said `project_network: true`."
          },
          "region": {
            "oneOf": [
              {
                "type": "string",
                "x-extensible-enum": [
                  "na",
                  "eu",
                  "ap"
                ]
              },
              {
                "type": "null"
              }
            ],
            "description": "The region of the node that holds it, as the node was told (`--region`): where a control plane placed it. Null on a node that is in no region."
          },
          "usable_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Where its disk came back from the object store (a restore, a move to this node, or a sandbox made here from a snapshot taken on another node, whose disk is in the store), the moment it could answer: its node had it and, for a disk filled as it is read, something listened for the blocks not here yet. Null for a sandbox whose disk was made on its node. Each such arrival starts the four fields over."
          },
          "hydrated_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "The moment every block of its disk was on its node after it came back from the object store; null until then, and for a sandbox whose disk was made there. Never before `usable_at`; set once for each arrival (`sandbox.hydrated` says so)."
          },
          "hydrated_bytes": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int64",
            "minimum": 0,
            "description": "How many bytes of its disk are on its node, of `total_bytes`, while it fills after it came back from the object store (a block brought ahead of the guest's first read counts); `total_bytes` once it is all there. Bytes, not files: what a few large files that come last hold shows as what is left. Null as `total_bytes` is."
          },
          "total_bytes": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int64",
            "minimum": 0,
            "description": "How many bytes its disk brings from the object store: its own, and its image's where its node did not have the image and it comes block by block too. Null for a sandbox whose disk was made on its node, and in the moment before its node has counted them."
          },
          "disk_writes": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/SandboxDiskWrites"
              },
              {
                "type": "null"
              }
            ],
            "description": "What its disk has written today (UTC), against the day's share at full speed: past the share its disk takes writes slowly until the day ends, and no write fails. Null on a node that does not copy its disks to an object store, which counts nothing and slows nothing, and for a static instance (`SandboxInternals.static`), whose disk is not copied."
          },
          "url_secret": {
            "type": "string",
            "pattern": "^[0-9a-f]{32}$",
            "description": "Signs this sandbox's preview links. Absent (not null) when the caller is a sandbox token."
          },
          "url_pattern": {
            "type": [
              "string",
              "null"
            ],
            "description": "The preview URL of a port, with `{port}` and `{signature}` to fill in: the signature is the first 12 characters of base32(HMAC-SHA256(key = `url_secret`, message = the port in decimal)), in the alphabet `0123456789abcdefghjkmnpqrstvwxyz`. For a public port (`public_ports`, or one `public_ports_except` does not name where it is given), drop `-{signature}`. Null when the node has no preview domain; absent when the caller is a sandbox token."
          }
        }
      },
      "SandboxStartCommand": {
        "type": "object",
        "additionalProperties": false,
        "description": "A sandbox's start command as its guest says it (guest/sbx-init.c, \"Started again\"). Where the node has its guest start it again (an instance that is its owner's state), that is once its process has ended and nothing it left behind holds its output, after a wait that doubles with every run that ends within 10 s of its start: 0.1 s after the first, then 0.2, 0.4 and so on, a minute at the most; a run of 10 s or more starts the waits from 0.1 s again (Docker's restart policy `always`, the same numbers). An end the node asks for (its stop: a delete, a restart from its disk, a change of version) is no reason to start it again.",
        "required": [
          "status",
          "restarts",
          "last_exit",
          "restarting_at"
        ],
        "properties": {
          "status": {
            "type": "string",
            "x-extensible-enum": [
              "running",
              "restarting",
              "exited"
            ],
            "description": "`running`: a run of it is under way (or what it left behind still holds its output); `restarting`: it ended, and is started again at `restarting_at`; `exited`: it ended, and nothing starts it again."
          },
          "restarts": {
            "type": "integer",
            "format": "int64",
            "minimum": 0,
            "description": "How often it was started again in its machine since the sandbox was made, across the boots of its machines."
          },
          "last_exit": {
            "oneOf": [
              {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "status",
                  "signal",
                  "at"
                ],
                "properties": {
                  "status": {
                    "type": "integer",
                    "description": "Its exit status as a shell says it: the code it ended with, or 128 + the signal's number where a signal ended it."
                  },
                  "signal": {
                    "type": [
                      "integer",
                      "null"
                    ],
                    "description": "The signal that ended it, where one did (`status` above 128); null where it exited."
                  },
                  "at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When it ended, by its guest's clock."
                  }
                }
              },
              {
                "type": "null"
              }
            ],
            "description": "How its last run ended; null where none has."
          },
          "restarting_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "While `restarting`: when its next run begins, by its guest's clock (a machine asleep then starts it as it wakes). Null otherwise."
          }
        }
      },
      "SandboxProxy": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "url",
          "username"
        ],
        "description": "A sandbox's own proxy as it is shown: never with its password, which the node keeps in the sandbox's record and writes into the requests for tunnels and nowhere else.",
        "properties": {
          "url": {
            "type": "string",
            "description": "`http://host:port`: the proxy's address, without the credentials."
          },
          "username": {
            "type": [
              "string",
              "null"
            ],
            "description": "The user name it is asked with; null where it is asked with none."
          }
        }
      },
      "CheckpointStatus": {
        "type": "object",
        "required": [
          "every_seconds",
          "last_at",
          "behind_seconds",
          "bytes_per_second",
          "stored_bytes",
          "error"
        ],
        "properties": {
          "every_seconds": {
            "type": "integer",
            "minimum": 1,
            "maximum": 600,
            "description": "The setting."
          },
          "last_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "The instant of its newest checkpoint in the store (when its machine was paused for it); null before the first."
          },
          "behind_seconds": {
            "type": "number",
            "minimum": 0,
            "description": "While it runs, how much longer ago than `every_seconds` its newest checkpoint was taken. It rises while the next one is on its way and drops when that one is in the store: while its checkpoints keep up it stays 0 or under what one takes to reach the store; past an interval, one took longer than an interval, and it grows on while it writes faster than they go to the store (or the store does not answer). 0 while it sleeps."
          },
          "bytes_per_second": {
            "type": "integer",
            "minimum": 0,
            "description": "What its checkpoints sent of late, bytes a second (compressed)."
          },
          "stored_bytes": {
            "type": "integer",
            "minimum": 0,
            "description": "What the store keeps of its newest checkpoint: its memory as it was, the state of its devices, and its disk's blocks the store had in no other copy then. Billed as a sleeping sandbox's state while it runs."
          },
          "error": {
            "type": [
              "string",
              "null"
            ],
            "description": "Why the last checkpoint was not taken or not sent, in a sentence; null when it was."
          }
        },
        "additionalProperties": false
      },
      "SandboxVolume": {
        "type": "object",
        "description": "A volume a sandbox has, as the sandbox object shows it.",
        "required": [
          "path",
          "volume",
          "name",
          "read_only",
          "shared"
        ],
        "properties": {
          "path": {
            "type": "string",
            "description": "Where it is mounted."
          },
          "volume": {
            "type": "string",
            "pattern": "^vol-[0-9a-hjkmnp-tv-z]{16}$",
            "description": "The volume's id (never its name)."
          },
          "name": {
            "type": [
              "string",
              "null"
            ],
            "description": "The volume's name, if it has one."
          },
          "read_only": {
            "type": "boolean"
          },
          "shared": {
            "type": "boolean",
            "description": "True for a shared volume (served by the node over NFS), false for one that is this sandbox's alone (a block device)."
          },
          "kind": {
            "type": [
              "string",
              "null"
            ],
            "x-extensible-enum": [
              "drive",
              "plain",
              "shared"
            ],
            "description": "The volume's kind (`Volume.kind`): a Drive goes with the sandbox when it moves to another node (slice D2), any other kind keeps it where it is. Null for one this node does not have."
          },
          "view_of": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": false,
            "required": [
              "drive",
              "version"
            ],
            "description": "Of a read-only view of a version of a Drive (slice D7: `volumes` with `version`): the Drive, and the version. The view is the sandbox's alone, and goes with it.",
            "properties": {
              "drive": {
                "type": "string",
                "pattern": "^vol-[0-9a-hjkmnp-tv-z]{16}$"
              },
              "version": {
                "$ref": "#/components/schemas/DriveVersion"
              }
            }
          }
        }
      },
      "Egress": {
        "type": "object",
        "description": "A sandbox's egress rules, as they are stored: what it may reach, what it may ask of a host, and which of its requests get a secret added. Always there; a sandbox without rules has `default` `allow` and four empty lists, and its traffic takes the path it would take without rules. A connection or a packet that `deny` speaks of is refused; one that `allow` speaks of passes; anything else gets `default`. A host in `inject` counts as allowed, and so does a host in `http`, for the requests its entry allows. Whatever the rules say, a sandbox reaches neither the node, nor other sandboxes, nor private ranges, nor the metadata addresses of clouds.\n\nA host name is matched against the server name of a TLS handshake and the Host header of an HTTP request, on any port, and against the sandbox's DNS questions, which the node answers itself for a sandbox whose rules can refuse a name (for a refused name the answer is `REFUSED`). A connection that names an allowed host goes to an address the node has for that name, whatever address the sandbox was connecting to. A TCP connection that is neither TLS nor HTTP passes to an address the rules list, or to one the node gave the sandbox as that of an allowed name. UDP and every other protocol go by address only. Under `default: allow` a `deny` list is not a wall: anything allowed that relays traffic gets around it.",
        "required": [
          "default",
          "allow",
          "deny",
          "http",
          "inject"
        ],
        "properties": {
          "default": {
            "type": "string",
            "enum": [
              "allow",
              "deny"
            ],
            "description": "What happens to what neither list speaks of."
          },
          "allow": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/EgressEntry"
            },
            "description": "Reached under `default: deny`. Empty under `default: allow`."
          },
          "deny": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/EgressEntry"
            },
            "description": "Never reached, whatever `allow` says."
          },
          "http": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/EgressHttp"
            },
            "description": "The requests the sandbox may send to a host, by method and path. Methods are shown in capitals, each once; the rest as it was given."
          },
          "inject": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/NodeEgressInject"
            },
            "description": "Headers the node adds to the sandbox's HTTPS requests to a host."
          },
          "deny_ports": {
            "type": "array",
            "items": {
              "type": "integer",
              "minimum": 1,
              "maximum": 65535
            },
            "description": "Ports no connection reaches, TCP or UDP, wherever it goes and whatever else the rules say: refused first, by the packet filter, and never handed to the proxy. Shown only where the rules close any. A control plane closes a Browsers session's mail and mining-pool ports so."
          }
        },
        "additionalProperties": false
      },
      "NodeSandboxLimits": {
        "type": "object",
        "description": "The shape of the machine: what its create asked for in `limits`, each part it did not give the node's base template's; a sandbox made from a snapshot or a template has that one's. The disk's size is the node's, for every sandbox.",
        "required": [
          "vcpus",
          "memory_bytes",
          "disk_bytes"
        ],
        "properties": {
          "vcpus": {
            "type": "integer",
            "minimum": 0,
            "description": "Virtual CPUs."
          },
          "memory_bytes": {
            "type": "integer",
            "format": "int64",
            "minimum": 0,
            "description": "Memory the machine has."
          },
          "disk_bytes": {
            "type": "integer",
            "format": "int64",
            "minimum": 0,
            "description": "The size of the root disk. 0 while a sandbox is `starting` and has no disk yet."
          }
        }
      },
      "SandboxUsage": {
        "type": "object",
        "description": "What the sandbox has used and uses.",
        "required": [
          "cpu_seconds",
          "memory_gib_seconds",
          "memory_bytes",
          "disk_bytes",
          "egress_bytes",
          "egress_refused_requests",
          "awake_seconds",
          "proxy_sent_bytes",
          "proxy_received_bytes"
        ],
        "properties": {
          "proxy_sent_bytes": {
            "type": "integer",
            "format": "int64",
            "minimum": 0,
            "description": "Bytes the sandbox sent through its own proxy since it was created (`proxy`): what went into its tunnels, counted as the proxy was given each byte (the requests for the tunnels not among them). 0 for one that never had one."
          },
          "proxy_received_bytes": {
            "type": "integer",
            "format": "int64",
            "minimum": 0,
            "description": "Bytes that came back to the sandbox through its own proxy since it was created, counted as each was taken from the proxy, whether or not the sandbox's end of the connection was still there to take it (what the proxy counts it sent)."
          },
          "awake_seconds": {
            "type": "number",
            "minimum": 0,
            "description": "How long a machine of the sandbox has run since it was created, to the millisecond: its time awake. Not while it sleeps, nor while the pause tier holds its machine paused. The same milliseconds its memory is counted over (`memory_gib_seconds`). The node's books count it hour by hour (`awake_ms` of a slice of `GET /internal/inventory`)."
          },
          "egress_refused_requests": {
            "type": "integer",
            "format": "int64",
            "minimum": 0,
            "description": "How many of the sandbox's HTTP requests the node has read and not passed on since the sandbox was created: the ones its own `egress` rules refuse, the ones it could not read in one way, and the ones that came while its owner's sandboxes had their share of the proxy's connections (the answers of `x-egress-proxy` with a status of 400 or 403, 502 `secret_missing` and 503 `egress_connections_full`). Counted once a second. Each is also a line of the node's log, with the id the answer carried, the method, the host and the path, without the query; of one sandbox's, at most 30 a minute are written."
          },
          "cpu_seconds": {
            "type": "number",
            "minimum": 0,
            "description": "CPU time the sandbox's machine has used since the sandbox was created: its vCPUs and the devices that serve them. Time the host spends waiting on the guest's behalf with a CPU is left out: the hypervisor polling for a halted vCPU to be woken, and the block device looking for the next request. What is left out never exceeds what has been counted: at least half of the CPU time the machine's process used is the sandbox's."
          },
          "memory_gib_seconds": {
            "type": "number",
            "minimum": 0,
            "description": "The memory the sandbox holds on the host by itself (`memory_bytes`), rounded up to 128 MiB steps, integrated over the time the sandbox was awake or paused (`sleep_tier` `paused`): GiB times seconds."
          },
          "memory_bytes": {
            "type": "integer",
            "format": "int64",
            "minimum": 0,
            "description": "Memory the host holds for this sandbox alone now: what the guest has written of its template's memory, memory the template never had, and the VMM's own. Pages of the template that the guest has only read are shared by every sandbox of the template and not counted. Of a paused sandbox, what its machine holds standing still; 0 while the sandbox sleeps otherwise."
          },
          "disk_bytes": {
            "type": "integer",
            "format": "int64",
            "minimum": 0,
            "description": "Bytes the root disk takes on the node now."
          },
          "egress_bytes": {
            "type": "integer",
            "format": "int64",
            "minimum": 0,
            "description": "Bytes the sandbox has sent to the internet since it was created: what it sent itself, and what went out to visitors of its preview URLs."
          }
        }
      },
      "SandboxHostFailure": {
        "type": "object",
        "description": "What a sandbox lost to its host: its memory, to a host that went down hard, to a node that was started again with another Firecracker or other settings for its machines' devices than the memory had been saved with, or to a load that failed. The sandbox keeps its id, its name, its settings, its links and its volumes; what ran in it is gone, and it starts from its disk at the next call that needs it (`booted_at` changes then).",
        "required": [
          "at",
          "cause",
          "memory",
          "disk",
          "disk_as_of",
          "detail"
        ],
        "properties": {
          "at": {
            "type": "string",
            "format": "date-time",
            "description": "When the node found the sandbox like this. With `cause` `hard_stop` that is when the node came back; the host went down some time before."
          },
          "cause": {
            "type": "string",
            "x-extensible-enum": [
              "hard_stop",
              "node_changed",
              "load_failed",
              "rollback_interrupted",
              "moved"
            ],
            "description": "`hard_stop`: the host went down without warning (power, a kernel panic). `node_changed`: nothing went down. The sandbox's memory, or the memory of the snapshot it was created from or rolled back to, was whole, and had been saved by a build of Firecracker that the host does not keep (a build it keeps wakes what it saved: `Node.firecrackers`; `detail` names the build), or with other settings for the machine's devices than the node starts machines with now (after an upgrade of the node, for one). A saved machine is loaded only into what saved it: anything else would run its guest against devices that are not the ones it had, which damages what it reads and writes without an error anywhere. The node keeps such memory until the sandbox is next used, so that a node that is started again with what saved it wakes the sandbox as it was; at the first use on a node that cannot load it, the memory is given up and this is set. `load_failed`: nothing went down, the memory was whole and was one this node loads, and it could not be brought back all the same (after a change of the host's kernel, for one: `detail` names the kernel the memory was saved under and the one that was to load it). Memory is given up for this on evidence only: the node's Firecracker refused to load it; the process that was to load it ended over the load; the machine ended as soon as it was loaded; the machine was loaded, did not answer, and could not be saved again; or the machine was loaded, stayed alive and did not answer, a second time five minutes or more after the first with no wake that worked in between. A wake that fails before anything was loaded leaves the memory as it was, and so does a first silence, and every silence in the five minutes after it: those calls are answered 503 `unavailable` and can be made again. The call that finds the memory gone is answered by the machine that is then started from the disk. `rollback_interrupted`: the node stopped during a roll-back. Its forced disk replacement was replayed before this sandbox was served; its previous memory was discarded, and it starts from the snapshot's disk. The replacement intent stays until the loss record and the stopped block are on disk. `moved`: the sandbox was moved to this node from another (`POST /internal/sandboxes/{id}/takeover`) and came without its memory, because the store had no memory image of the very state its disk was brought back to: it had woken since its last sleep, or was running when the other node's host was lost, or the image could not be read (`detail` says which). Its disk is that state (`disk` `restored`)."
          },
          "memory": {
            "type": "string",
            "x-extensible-enum": [
              "lost"
            ],
            "description": "`lost`: what the sandbox had in memory is gone, its processes included. With `hard_stop`: either its machine was running, or it had gone to sleep so shortly before that its memory was not yet whole on the host's disk; a memory image that is not whole is never woken. With `node_changed`: the memory was whole and was not loaded. With `load_failed`: the memory was whole, and loading it failed. With `moved`: no image of its memory could be loaded over the disk it came with."
          },
          "disk": {
            "type": "string",
            "x-extensible-enum": [
              "unverified",
              "restoring",
              "restored",
              "whole"
            ],
            "description": "What the sandbox's disk is now. `whole`: nothing went down, and the disk lost nothing (`node_changed`: it is as it was when the memory was saved; `load_failed`: it has, besides, whatever a machine that was loaded and did not answer wrote before it was ended). `unverified`: the disk on the host, which was not known to be whole: the guest's file system replays its journal when the sandbox starts, and what was written in the last half minute before the host went down may be missing or incomplete, whether or not the guest had been told it was written. `restoring`: the node copies disks to an object store, and the sandbox's disk is being brought back from the copy there; calls that need the sandbox answer 503 `unavailable` until that is done. `restored`: the disk is the copy the object store had, a state the disk was really in, as of `disk_as_of`."
          },
          "disk_as_of": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "With `disk` `restored`: the moment the disk went back to. What was written after it is gone. Null otherwise."
          },
          "detail": {
            "type": "string",
            "description": "The same in a sentence for people."
          }
        },
        "additionalProperties": false
      },
      "SandboxDiskWrites": {
        "type": "object",
        "description": "A sandbox's writes of the day to its own disk. Counted as the copies of its disk carry them to the store: each block written since the copy before (every quarter of a second or so) once, as its whole 4 KiB, so a block written many times in between counts once. And what the copies its checkpoints made of its memory and disk wrote to the host's disk (`checkpoint_copy_bytes`): a checkpoint's copy goes once the store has it, and only what the host wrote out of it before then counts. The count goes on across sleeps and wakes and restarts of the node. What it writes to its Drives is not counted here, and its Drives are not slowed past the share: a Drive's writes are billed by the gigabyte (its own meter, `written_bytes` of the inventory's volume rows).",
        "required": [
          "today_bytes",
          "checkpoint_copy_bytes",
          "full_speed_until_bytes",
          "limited",
          "limited_until"
        ],
        "properties": {
          "today_bytes": {
            "type": "integer",
            "format": "int64",
            "minimum": 0,
            "description": "Bytes written since the day began (UTC)."
          },
          "checkpoint_copy_bytes": {
            "type": "integer",
            "format": "int64",
            "minimum": 0,
            "description": "Of `today_bytes`, what its checkpoints' copies wrote to the host's disk (0 while the store takes each copy before the host writes it out). Where its disk takes writes at a limited rate, its next checkpoint waits until these bytes have gone at that rate."
          },
          "full_speed_until_bytes": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int64",
            "minimum": 0,
            "description": "The day's share at full speed: 100 GiB unless the node is told otherwise (`--disk-write-full-gib-per-day`). Null: the node slows nothing."
          },
          "limited": {
            "type": "boolean",
            "description": "Past the day's share: the disk takes writes at 2 MiB a second (`--disk-write-after-mib-per-second`), or at the sandbox's own limit where that is lower, until the day ends."
          },
          "limited_until": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the day ends and the disk takes writes at full speed again; null when it is not limited."
          }
        }
      },
      "Cursor": {
        "type": [
          "string",
          "null"
        ],
        "description": "Pass it as `cursor` to get the next page. Null on the last page."
      },
      "SandboxCreate": {
        "type": "object",
        "description": "Every field may be left out; an empty body or none creates a sandbox from the node's base image. At most one of `image`, `snapshot` and `template`. A field given as null counts as not given (for `egress` under `snapshot`, null says something: see there).",
        "properties": {
          "start": {
            "type": "boolean",
            "description": "`true`: start the image's own start command (ENTRYPOINT and CMD) when the machine boots; `false`: start nothing, whatever the image's labels ask for. Given (as any of `start`, `start_command` and `ready`), it is all that counts: the image's labels `sbx.start` and `sbx.ready` are not read. The sandbox is made from a template built with these settings and `env` (two settings of one image are two templates; the first sandbox of a setting is returned `starting` while it is built), which only its owner's sandboxes are made from. Not for a sandbox made from a `snapshot` or a `template` (400 `invalid_start`)."
          },
          "start_command": {
            "description": "The start command, run as the image's user, its output kept by the sandbox's agent as that of the command `exe-start` (the last 16 MiB of it, read like any command's with `GET …/exec/exe-start` and `…/exec/exe-start/output`; for an app's instance it is also what the app's log has of the instance): a string is run by `/bin/sh -c` and may hold several lines; a list is run as it is, without a shell (for an image that has none). At most 1000 arguments and 64 KiB, no NUL bytes, the first argument not empty. With `start: false`, 400 `invalid_start`.",
            "oneOf": [
              {
                "type": "string",
                "minLength": 1,
                "maxLength": 65536
              },
              {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "minItems": 1,
                "maxItems": 1000
              }
            ]
          },
          "ready": {
            "$ref": "#/components/schemas/NodeReady"
          },
          "egress": {
            "$ref": "#/components/schemas/NodeEgressRules",
            "description": "What the sandbox may reach, what it may ask of a host, and which of its requests get a secret added. In force before the sandbox runs anything. A sandbox made from a `snapshot` starts with the rules the snapshot recorded unless this field is given; null then means no rules."
          },
          "guest_mitigations": {
            "type": "boolean",
            "description": "Whether the guest kernel keeps its mitigations against speculative execution. They guard the guest's kernel against processes in the same sandbox: turn them on when a sandbox runs code of several users who do not trust each other. Sandboxes are kept apart from each other by the host either way. Default: the node's setting (off). The setting is fixed when the kernel boots: the first sandbox with a setting other than its image template's starts like a new image (returned `starting`), and a sandbox made from a snapshot keeps the snapshot's (asking for the other is 400)."
          },
          "tmp": {
            "type": "string",
            "enum": [
              "disk",
              "memory"
            ],
            "default": "disk",
            "description": "Where the sandbox's /tmp is. `disk`: on its disk, as the rest. `memory`: a tmpfs of at most half of its memory, mounted before its first command (in a sandbox made from a template or an image, over what the template's guest had put there; by the guest's init where it starts from its disk): what is written there is the guest's memory, billed as memory, kept when it sleeps, and gone when it starts again from its disk. A sandbox made from a snapshot whose sandbox had it in memory has it so whatever this says."
          },
          "runtime": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/NodeRuntime"
              },
              {
                "type": "null"
              }
            ],
            "description": "Runtime settings of its machine and its commands, as a Compose file's `ulimits`, `sysctls`, `tmpfs`, `dns` and `shm_size` ask for them (`Runtime`). For a sandbox made from an image: they make a template of the caller's own, built with them (with nothing said of the start, the image is started as its labels ask). Refused for one made from a snapshot or a template (400 `invalid_start`), which goes on with what its machine has."
          },
          "init": {
            "oneOf": [
              {
                "type": "string",
                "enum": [
                  "systemd"
                ]
              },
              {
                "type": "null"
              }
            ],
            "description": "What the sandbox's PID 1 is. Not given, or null: the platform's init, which starts the image's start command where there is one (`start`, `start_command`, the image's labels). `systemd`: a whole system. The image's own systemd is PID 1 and the node's agent is one of its services: the user installs packages, starts services with `systemctl`, uses cron and the journal and logs in by SSH, as on a machine. Nothing a create names is started (`start`, `start_command` and `ready` are refused with 400 `invalid_start`), and the template is saved once systemd says the machine has booted. Without `image`: the node's image of a whole system (`--systemd-image`; Ubuntu 24.04 with systemd and OpenSSH, sandboxd/images/ubuntu), refused with 400 `invalid_start` on a node that has none. An image without systemd (/usr/lib/systemd/systemd) fails its build, with `failure_code` `invalid_image`. Its template is anybody's who asks for the same image so, unless something of the caller's is in it (`runtime`, `image_pull_secret`). Refused for one made from a snapshot or a template (400 `invalid_start`), which goes on as its machine was. Everything else a sandbox does works as for any sandbox: commands, files, terminals, the SSH gateway, sleep and wake, snapshots and forks, moves, egress rules (sandboxd/README.md, 「整个系统：systemd 当 PID 1」)."
          },
          "project_network": {
            "type": "boolean",
            "description": "`true`: the sandbox is on its project's private network from before it runs anything, at the first of the addresses the node was left to spare for the project (`setProjectNetwork`): the answer says which (`project_address`). The project is the call's owner, so this is for a call the control plane makes for a project; on a node that stands alone a sandbox is nobody's, and is put on a network by being named in it. A sandbox is on no project's network unless it asks: default `false`. A copy made by a fork is on none, whatever its original is on."
          },
          "image": {
            "type": "string",
            "maxLength": 512,
            "description": "An OCI image reference (`python:3.12`, `ghcr.io/org/app@sha256:…`), in a registry on the internet: one that gives the image to anybody, or one that wants a login, with `image_pull_secret`. The first sandbox of an image is returned `starting` while the node pulls the image and builds a template; later ones start from the template. Default: the node's base image."
          },
          "image_pull_secret": {
            "type": "string",
            "maxLength": 128,
            "description": "The name of a secret (`PUT /v1/secrets/{name}`) that holds the credentials of the image's registry, for an image that is not public: `username:password` in printable ASCII, the user name ending at the first colon. A registry that takes a token alone documents the user name that goes with it (`AWS` for ECR, `oauth2accesstoken` for Google's, any name for GHCR). The node hands the credentials to the tool that pulls the image and to nothing else: nothing of them is in the sandbox, in its template or in the node's log, and what a registry says when it refuses them comes back without them (`failure_code` `registry_auth_failed`). The sandbox is made from a template of its owner's alone, which a create that names credentials finds and no other: the same image named without them is asked of its registry without a login, whether or not the node has the template. The secret has to exist (404 `secret_not_found`), to be one that may go to the image's registry (its `allowed_hosts` cover the registry's host as the reference writes it, without its port, `docker.io` where it names none; 403 `secret_host_not_allowed` otherwise) and to hold such a pair (400 `invalid_field`), and it goes with `image` (400 `invalid_field` for a sandbox made from none)."
          },
          "snapshot": {
            "type": "string",
            "description": "A snapshot's id or name: the sandbox goes on from the instant the snapshot was taken, processes included."
          },
          "template": {
            "type": "string",
            "description": "A template's key on the node (`base`, `img-…`), as `GET /internal/node` lists them. Not in the reference README; the benchmark scripts use it."
          },
          "limits": {
            "$ref": "#/components/schemas/SandboxLimitsCreate"
          },
          "name": {
            "$ref": "#/components/schemas/Name"
          },
          "env": {
            "$ref": "#/components/schemas/Env"
          },
          "secret_env": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "maxProperties": 64,
            "description": "More environment variables, whose values are secrets: every command started in the sandbox gets them with `env`, and so does its start command (written for it where `env` is, and nowhere else: no value of them is in a template's key or record). No answer shows a value: the sandbox shows their names (`Sandbox.secret_env`). At most 64 variables and 64 KiB, names as `env` takes them, none of them in `env` too (400 `invalid_field`, `param` `secret_env.<name>`); taken when the sandbox is made and not changed afterwards (a PATCH that gives `env` one of these names is refused the same way, `param` `env.<name>`). Not in a snapshot: a sandbox made from one has the ones its own create gives."
          },
          "metadata": {
            "$ref": "#/components/schemas/Metadata"
          },
          "volumes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/VolumeMountCreate"
            },
            "description": "Volumes to mount before the call returns. At most two that are not shared. A sandbox made from a template that holds its start command back (`TemplateBuildRequest.start_after_volumes`) has its start command started once they are mounted, and the call returns when the command is up as the template's `ready` says; so does every boot of it from its disk (a restart, a host that went down), which mounts them first."
          },
          "public_ports": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Port"
            },
            "maxItems": 64,
            "description": "Ports anybody may open at their short preview URL."
          },
          "public_ports_except": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "$ref": "#/components/schemas/Port"
            },
            "maxItems": 64,
            "description": "Every port but these is public as well: its short preview URL opens it for anybody, as a port in `public_ports` (how E2B makes a sandbox's ports; the control plane's E2B compatibility gives `[49983]`, envd's port, which a sandbox token opens). `[]`: every port. Null (the default): only `public_ports`. Ports are taken once each, in order."
          },
          "awake_ports": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "$ref": "#/components/schemas/Port"
            },
            "maxItems": 16,
            "description": "Ports on which a connection upgraded through the preview URL (a WebSocket: a browser's DevTools client on 9222, the viewer of a screen on 6080) keeps the sandbox awake for as long as it is open, and is billed for. While this names a port, the sandbox is idle by those connections and by the requests and calls it is given, and by nothing its guest does: its CPU, its timers, its connections to anywhere and their traffic do not keep it awake, and it is not woken for a timer (what came due meanwhile fires when the next connection wakes it). It goes to sleep `sleep_after_seconds` after the last of them ended, without its guest being asked. Such a connection has TCP's keepalive on its client's side, so that a client that went away without closing it is found out within about a minute. Null or `[]` (the default): idle as any sandbox is. Kept in order, without duplicates. Not for a one-shot run (`POST /v1/run`). A session of Browsers (reports/browsers/README.md) is made with `[9222]`."
          },
          "connection_idle_seconds": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 1,
            "maximum": 86400,
            "description": "A connection on one of `awake_ports` that has carried nothing in either direction for this long is closed by the node (and the sandbox, if nothing else holds it, sleeps `sleep_after_seconds` after that). Null (the default): never for that. Holds for the connections that open after it is set."
          },
          "inbound": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "$ref": "#/components/schemas/InboundPort"
            },
            "maxItems": 64,
            "description": "Ports open to the internet at the sandbox's own IPv6 address from the start, each with its sources (`…/tcp/{port}` opens one later). A node without a pool of addresses refuses a list that is not empty: 409 `no_ipv6`."
          },
          "sleep_after_seconds": {
            "type": [
              "number",
              "null"
            ],
            "minimum": 1,
            "maximum": 1000000000,
            "default": 30,
            "description": "Sleep after this long idle. Null: never by itself. The default is the node's (`--sleep-after-seconds`, 30 unless set)."
          },
          "expires_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Delete the sandbox at this time, which must be in the future (or what `on_expiry` says). Null or absent: never."
          },
          "on_expiry": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "delete",
              "sleep",
              "hold",
              null
            ],
            "default": "delete",
            "description": "What `expires_at` does when it comes: `delete` the sandbox (null or absent: this); `sleep`, put it to sleep whatever goes on in it, and keep it (its `expires_at` is null from then on, and a request wakes it as ever); `hold`, the same, held asleep (`held_asleep` is set then: a request does not wake it). How E2B pauses a sandbox when its time is up (the control plane's E2B compatibility). Another word is 400 `invalid_field`."
          },
          "held_asleep": {
            "type": [
              "boolean",
              "null"
            ],
            "default": false,
            "description": "True: while it sleeps, a request to one of its ports does not wake it: the preview listener answers 502 `sandbox_paused` in its place, as E2B's proxy answers for a paused sandbox (`x-preview-ingress`), and only a call of the API (or its own timer) wakes it. It is not put to sleep by this: `POST /v1/sandboxes/{id}/sleep` does that. False or null: as any sandbox. (E2B's `pause` without its `autoResume`, which the control plane's E2B compatibility sets, and takes away at its `connect`.)"
          },
          "sleep_notice_ms": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "maximum": 60000,
            "description": "How long before the idle rule puts the sandbox to sleep (or pauses it) every program connected to its guest's `/run/sbx/awake` is sent the line `sleep N`, in milliseconds; the sleep comes that long after, unless a hold raised meanwhile puts it off (`sleep_hold_max_ms`). Where nobody is connected, the sleep is not put off. A sleep somebody asks for (`POST /v1/sandboxes/{id}/sleep`, a freeze, a move, the node stopping) is told nothing. 0, null or absent: not told."
          },
          "sleep_hold_max_ms": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "maximum": 900000,
            "description": "How long the holds a program in its guest raises (`+` on `/run/sbx/awake`) keep the sandbox up at the most, in milliseconds, counted from the moment it was first found held while nothing else kept it up; then it sleeps whatever holds it (the programs connected are told `sleep N cap` first, where `sleep_notice_ms` asks for a notice), and the count begins again at its next quiet. Held, it runs and is metered as running. 0: holds are not heeded. Null or absent: the node's (`--sleep-hold-max-seconds`, 30 s unless set)."
          },
          "suspend_after_ms": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 1000,
            "maximum": 86400000,
            "description": "How long the pause tier (`pause_tier` of `GET /internal/node`) holds the sandbox paused, once the idle rule has stopped it, before its memory is saved and it sleeps for real, in milliseconds. Paused, it answers in a millisecond or two and is metered for the memory it holds; asleep, for what it keeps on disk. The node's `--pause-max-seconds` (600 unless set) is also the longest it is held paused whatever this says: a paused machine holds the host's memory. The host's memory below the floor, a slot another machine needs and a sleep somebody asks for put it to sleep sooner. Null or absent: the node's. Of no effect where the node has no pause tier."
          },
          "wake_daily_at": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^([01][0-9]|2[0-3]):[0-5][0-9]$",
            "description": "A time of day in UTC, `HH:MM`, at which the node wakes the sandbox every day while it sleeps or stands paused, for work of its own that waits for a wake and keeps no time (a database's maintenance, say). The wake is no activity: what runs then is, and once that is done the idle rule puts it to sleep again. Not for a sandbox that is idle by its connections (`awake_ports`). A stateful service's instance (its start command held back for its volumes) is woken besides for the next run of its PostgreSQL server's `pg_cron` jobs, which the node asks its agent for before it rests; while a job runs, or is due within 5 s, the idle rule does not rest it. Null or absent: none."
          },
          "delete_after_idle_ms": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int64",
            "minimum": 1000,
            "description": "Delete the sandbox once it has slept for this long, in milliseconds (`Sandbox.idle_since`); a wake starts the count again at the next sleep. Null or absent: never for that. Less than 1000 is refused (400 `invalid_field`)."
          },
          "hostname": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 63,
            "pattern": "^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$",
            "description": "The name the sandbox's machine has, for good, in place of its id: what `hostname` says in it, from its first boot and after every restart. Lower-case letters, digits and hyphens, in labels between dots; otherwise 400 `invalid_field`. The control plane gives a service's instance the service's name (or the service's `hostname`). Not given: the sandbox's id."
          },
          "stop": {
            "$ref": "#/components/schemas/SandboxStop"
          },
          "log": {
            "type": [
              "object",
              "null"
            ],
            "description": "The app's log the sandbox's lines go to, as the lines of the app's instances do (`readAppLog`): what its start command prints, a line for each request through its ports and what the node does to it (made, stopped, deleted). For a sandbox made from the app's version's template (`template`, the key the log knows the version by), of its owner's: the control plane gives it for an app's keyed instance and for a stateful service's. Nothing of the app's group holds for it otherwise: it is no instance the app's requests are given to, and sleeps and is deleted as a sandbox. Without `template`: 400 `invalid_field`.",
            "required": [
              "app"
            ],
            "properties": {
              "app": {
                "type": "string",
                "minLength": 1,
                "maxLength": 64,
                "description": "What the control plane calls the app: the log's name, as `metadata.app` of `setApp` gives it."
              },
              "version": {
                "type": [
                  "string",
                  "null"
                ],
                "minLength": 1,
                "maxLength": 64,
                "description": "And the version, which its lines say."
              }
            },
            "additionalProperties": false
          },
          "checkpoint": {
            "$ref": "#/components/schemas/CheckpointSetting"
          },
          "region": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "na",
              "eu",
              "ap",
              null
            ],
            "description": "The region to make it in, read by the API and not passed on. Null or left out: the region the API itself runs in. Where no machine of that region can take it, the next region does; what has to go to one machine (made from a snapshot or with volumes, or sent again with the same `Idempotency-Key`) goes there, whatever the region. A volume made from a backup is made in the backup's region: another is 400 `invalid_field`. Not a region: 400 `unknown_region`. The answer's `region` says where it was made."
          }
        },
        "additionalProperties": false
      },
      "SandboxUpdate": {
        "type": "object",
        "description": "A merge: fields left out stay as they are. Done whole or not at all.",
        "properties": {
          "egress": {
            "$ref": "#/components/schemas/NodeEgressRules",
            "description": "Merged into the sandbox's rules key by key; null takes every rule away. Holds at once for a sandbox that runs, for the connections it has as for those it makes: one that the new rules would not have let through is ended, and so may be one that was opened before the sandbox had rules by name. Does not wake a sandbox that sleeps: the rules are what it wakes to."
          },
          "name": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/Name"
              },
              {
                "type": "null"
              }
            ],
            "description": "Null removes the name."
          },
          "env": {
            "type": [
              "object",
              "null"
            ],
            "description": "Merged key by key: a string sets a variable, null removes it. Null for the whole field empties the environment. Commands started afterwards see the result.",
            "additionalProperties": {
              "type": [
                "string",
                "null"
              ]
            }
          },
          "metadata": {
            "type": [
              "object",
              "null"
            ],
            "description": "Merged key by key like `env`.",
            "additionalProperties": {
              "type": [
                "string",
                "null"
              ]
            }
          },
          "volumes": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "$ref": "#/components/schemas/NodeVolumeMount"
            },
            "description": "The whole list as it should be: volumes not in it are unmounted and detached, new ones attached, ones that are unchanged are left alone. Null or `[]` detaches all. Wakes the sandbox."
          },
          "public_ports": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "$ref": "#/components/schemas/Port"
            },
            "maxItems": 64,
            "description": "Replaces the list. Null empties it."
          },
          "proxy": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/SandboxProxyGiven"
              },
              {
                "type": "null"
              }
            ],
            "description": "Another proxy, or none (null), from now on: for a sandbox that runs, its connections through the one before end (their clients connect again, through this one), and nothing in the guest is started again."
          },
          "public_ports_except": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "$ref": "#/components/schemas/Port"
            },
            "maxItems": 64,
            "description": "Replaces the list of the ports that are not public while every other is (`SandboxCreate`). Null: only `public_ports` are public again."
          },
          "awake_ports": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "$ref": "#/components/schemas/Port"
            },
            "maxItems": 16,
            "description": "As in `SandboxCreate`: replaces the list; null or `[]` empties it, and the sandbox is idle as any sandbox is from then on. A connection opened while its port was in the list keeps the sandbox awake until it ends."
          },
          "connection_idle_seconds": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 1,
            "maximum": 86400,
            "description": "As in `SandboxCreate`; null: never. Holds for the connections that open after it."
          },
          "inbound": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "$ref": "#/components/schemas/InboundPort"
            },
            "maxItems": 64,
            "description": "Replaces the list of ports open at the sandbox's own IPv6 address. Null empties it. A sandbox without an address of its own refuses a list that is not empty: 409 `no_ipv6`."
          },
          "sleep_after_seconds": {
            "type": [
              "number",
              "null"
            ],
            "minimum": 1,
            "maximum": 1000000000,
            "description": "Null: never sleep by itself (not: back to the default)."
          },
          "expires_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Null: never."
          },
          "on_expiry": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "delete",
              "sleep",
              "hold",
              null
            ],
            "default": "delete",
            "description": "What `expires_at` does when it comes: `delete` the sandbox (null or absent: this); `sleep`, put it to sleep whatever goes on in it, and keep it (its `expires_at` is null from then on, and a request wakes it as ever); `hold`, the same, held asleep (`held_asleep` is set then: a request does not wake it). How E2B pauses a sandbox when its time is up (the control plane's E2B compatibility). Another word is 400 `invalid_field`."
          },
          "held_asleep": {
            "type": [
              "boolean",
              "null"
            ],
            "default": false,
            "description": "True: while it sleeps, a request to one of its ports does not wake it: the preview listener answers 502 `sandbox_paused` in its place, as E2B's proxy answers for a paused sandbox (`x-preview-ingress`), and only a call of the API (or its own timer) wakes it. It is not put to sleep by this: `POST /v1/sandboxes/{id}/sleep` does that. False or null: as any sandbox. (E2B's `pause` without its `autoResume`, which the control plane's E2B compatibility sets, and takes away at its `connect`.)"
          },
          "sleep_notice_ms": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "maximum": 60000,
            "description": "As in `SandboxCreate`. 0 or null: its guest is not told."
          },
          "sleep_hold_max_ms": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "maximum": 900000,
            "description": "As in `SandboxCreate`. Null: back to the node's."
          },
          "suspend_after_ms": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 1000,
            "maximum": 86400000,
            "description": "As in `SandboxCreate`. Null: back to the node's."
          },
          "wake_daily_at": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^([01][0-9]|2[0-3]):[0-5][0-9]$",
            "description": "As in `SandboxCreate`. Null: none."
          },
          "delete_after_idle_ms": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int64",
            "minimum": 1000,
            "description": "After how long asleep the sandbox is deleted; null: never for that. Holds for the sleep it is in: one that has slept that long already is deleted within a second."
          },
          "checkpoint": {
            "$ref": "#/components/schemas/CheckpointSetting",
            "description": "As in `SandboxCreate`; null: none from now on. Holds from the next checkpoint of a sandbox that runs."
          }
        },
        "additionalProperties": false
      },
      "NodeVolumeMount": {
        "type": "object",
        "description": "A volume to mount, as a request gives it. Unknown keys are refused.",
        "required": [
          "path",
          "volume"
        ],
        "properties": {
          "path": {
            "type": "string",
            "description": "Where the volume is mounted in the sandbox: an absolute path other than `/`."
          },
          "volume": {
            "type": "string",
            "description": "The volume's id or name."
          },
          "read_only": {
            "type": [
              "boolean",
              "null"
            ],
            "default": false,
            "description": "Mount it read-only. For a shared volume this is enforced on the node, not in the sandbox."
          },
          "version": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/DriveVersion"
              },
              {
                "type": "null"
              }
            ],
            "description": "A version of the Drive `volume` names, as a snapshot of it says it (slice D7): the sandbox is given a read-only view of that version, its own, fetched from the store as it is read, whatever has the Drive and wherever it is (the Drive may be deleted since). Mounted read-only."
          }
        },
        "additionalProperties": false
      },
      "SandboxProxyGiven": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "url"
        ],
        "description": "A sandbox's own proxy: an HTTP proxy that takes `CONNECT`. Its TCP connections to ports 80 and 443 go out through it, from its first packet: the node turns them, in the sandbox's network slot, towards its egress proxy, which reads where each goes (the TLS handshake's server name, the HTTP request's `Host`; the address it was going to where they name none) and asks this proxy for a tunnel there (`CONNECT name:port`, with the credentials, Basic), and passes the bytes both ways unread. UDP to port 443 (QUIC, which no HTTP proxy carries) is refused, so browsers speak HTTP over TCP. Connections to other ports go as they went. Nothing in the guest is told of it, and nothing there holds the credentials. What goes through it is counted both ways (`usage.proxy_sent_bytes`, `proxy_received_bytes`); what leaves the node for the proxy is the sandbox's traffic to the internet, as any (`egress_bytes`). A connection the proxy does not take a tunnel for is told so (`x-egress-proxy`, 502 `proxy_failed`, or a TLS alert). Rules that have the node read requests (`egress.inject`, `egress.http`) do not go with a proxy: 400 `invalid_proxy`.",
        "properties": {
          "url": {
            "type": "string",
            "maxLength": 2048,
            "description": "`http://user:password@host:port` (the port 80 where it names none; the user name and the password are written %XX where they hold what a URL does not take, as `@` or `:`). The host is a name, which the node resolves, or an address on the internet: an address no sandbox reaches (a private range, link-local, loopback) is refused. Only `http://`: a proxy reached over TLS, and SOCKS, are not served (400 `invalid_proxy`, which never says the URL back)."
          }
        }
      },
      "NoFields": {
        "type": "object",
        "description": "No fields: an empty object, or no body at all.",
        "additionalProperties": false
      },
      "ForkRequest": {
        "type": "object",
        "properties": {
          "count": {
            "type": "integer",
            "minimum": 1,
            "maximum": 64,
            "default": 1,
            "description": "How many copies."
          }
        },
        "additionalProperties": false
      },
      "ForkResult": {
        "type": "object",
        "description": "The copies a fork made, in the envelope of every list. It has one page.",
        "required": [
          "data",
          "next_cursor"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Sandbox"
            },
            "description": "The copies, in order. A copy of a sandbox named `build` is named `build-1`, `build-2`, … (the next numbers that are free); copies of a sandbox without a name have none."
          },
          "next_cursor": {
            "type": "null",
            "description": "Always null."
          }
        }
      },
      "SnapshotCreate": {
        "type": "object",
        "properties": {
          "name": {
            "$ref": "#/components/schemas/Name"
          },
          "replace": {
            "type": "boolean",
            "default": false,
            "description": "If another snapshot has the name, take the name from it instead of answering 409. The other snapshot is kept, without a name."
          },
          "expires_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Delete the snapshot at this time. Null or absent: never."
          }
        },
        "additionalProperties": false
      },
      "Snapshot": {
        "type": "object",
        "description": "Memory, processes and disk of a sandbox as they were at one instant.",
        "required": [
          "id",
          "object",
          "name",
          "status",
          "memory_loadable",
          "sandbox_id",
          "image",
          "size_bytes",
          "volumes",
          "egress",
          "replicated",
          "replicated_at",
          "last_replication_status",
          "created_at",
          "expires_at"
        ],
        "properties": {
          "replicated": {
            "type": "boolean",
            "description": "Whether the snapshot is in the object store the node copies its disks to, complete (every part of it there and its index written last), so that a node of the site that copies to the same store can make a sandbox from it, fork from it and roll a sandbox back to it. A node that copies its disks (`--upload`) publishes every snapshot it takes, in the background, a second or two after the call that took it answered. Its disk is not copied: the publication pins the state the sandbox's own disk had in the store at the snapshot, and carries only the blocks that were not there yet; its memory goes as a sleep's image does. False until then, and for good for a snapshot of a node that copies to no store, one taken before snapshots were published, and one of a machine whose memory is not in layers: such a snapshot is used on its node, as ever."
          },
          "replicated_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When its publication was complete. Null: it is not in the store."
          },
          "last_replication_status": {
            "type": "string",
            "x-extensible-enum": [
              "pending",
              "replicating",
              "replicated",
              "failed",
              "not_replicable"
            ],
            "description": "How its publication stands: `pending` (taken, not asked of the node's uploader yet: its files are being forced), `replicating` (the uploader is at it), `replicated`, `failed` (the last try failed, and the node tries again, twenty seconds later and twice as long after each failure, at most a quarter of an hour), `not_replicable` (it is never published, for the reasons under `replicated`)."
          },
          "egress": {
            "$ref": "#/components/schemas/Egress",
            "description": "The egress rules of the sandbox at the instant the snapshot was taken. A sandbox created from the snapshot starts with them unless the call gives `egress`; a sandbox restored to the snapshot keeps the rules it has."
          },
          "memory_loadable": {
            "type": "boolean",
            "description": "Whether a sandbox made from the snapshot on this node continues from the snapshot's instant, processes included. False: the snapshot's memory was saved by a build of Firecracker the host does not keep, or with other settings for the machine's devices than the node starts machines with now (or the snapshot is `lost`); one that a kept build saved is loaded by that build (`Node.firecrackers`). The snapshot is kept as it is, and is loadable again on a node that is started with what saved it. Meanwhile a sandbox created from it, or rolled back to it, gets the snapshot's disk and starts from that: nothing that ran in the snapshot runs in it, and its `host_failure` says so (`cause` `node_changed`)."
          },
          "id": {
            "type": "string",
            "pattern": "^snap-[0-9a-hjkmnp-tv-z]{16}$"
          },
          "object": {
            "type": "string",
            "const": "snapshot"
          },
          "name": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/Name"
              },
              {
                "type": "null"
              }
            ]
          },
          "status": {
            "type": "string",
            "x-extensible-enum": [
              "ready",
              "lost"
            ],
            "description": "`ready`: the call that takes a snapshot returns when it is complete. `lost`: the node's host went down hard before the snapshot's files were on its disk (the node writes them there within a second or two of the call's answer, without holding the call up); it is listed so that its owner knows, sandboxes cannot be created from it or restored to it (409 `snapshot_lost`), and it can be deleted."
          },
          "sandbox_id": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^sbx-[0-9a-hjkmnp-tv-z]{16}$",
            "description": "The sandbox it was taken of."
          },
          "image": {
            "type": [
              "string",
              "null"
            ],
            "description": "The image that sandbox goes back to."
          },
          "size_bytes": {
            "type": "integer",
            "format": "int64",
            "minimum": 0,
            "description": "What it holds, in bytes, fixed when it is taken: what its sandbox had of its own at that moment (the pages of its memory that are not its template's, the state of its devices, the layer of its disk over the image) and nothing of the template or the image. The three parts a sleeping sandbox is counted as keeping. It is the same whatever else exists: another snapshot of the sandbox holds its own number, and deleting one changes no other's. A node counts a snapshot as keeping this for as long as it is there (a `lost` one as keeping nothing)."
          },
          "volumes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SnapshotVolume"
            }
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "expires_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When it deletes itself. Null: never."
          }
        }
      },
      "SnapshotVolume": {
        "type": "object",
        "description": "A shared volume the sandbox had mounted when the snapshot was taken. A sandbox created from the snapshot comes up with it mounted.",
        "required": [
          "path",
          "volume",
          "read_only"
        ],
        "properties": {
          "path": {
            "type": "string"
          },
          "volume": {
            "type": "string",
            "pattern": "^vol-[0-9a-hjkmnp-tv-z]{16}$"
          },
          "read_only": {
            "type": "boolean"
          }
        }
      },
      "RestoreRequest": {
        "type": "object",
        "required": [
          "snapshot"
        ],
        "properties": {
          "snapshot": {
            "type": "string",
            "description": "The id or name of the snapshot to go back to."
          }
        },
        "additionalProperties": false
      },
      "RestoreResult": {
        "type": "object",
        "required": [
          "sandbox",
          "before"
        ],
        "properties": {
          "sandbox": {
            "$ref": "#/components/schemas/Sandbox"
          },
          "before": {
            "$ref": "#/components/schemas/Snapshot",
            "description": "What the sandbox was just before, kept as a snapshot named `before-restore-…` so that the roll-back can be undone. It stays until it is deleted."
          }
        }
      },
      "TokenRequest": {
        "type": "object",
        "properties": {
          "scopes": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "url",
                "exec",
                "files",
                "tty"
              ]
            },
            "default": [
              "url"
            ],
            "description": "What the token may be used for. `url`: preview URLs and `GET …/url`. `exec`: commands. `files`: files. `tty`: terminals. An empty list is accepted and makes a token that can only read the sandbox object."
          },
          "expires_in_seconds": {
            "type": "number",
            "minimum": 1,
            "maximum": 2592000,
            "default": 3600,
            "description": "How long the token works: from 1 second to 30 days."
          },
          "e2b": {
            "type": "boolean",
            "default": false,
            "description": "True: the sandbox's one token for E2B's SDKs (the control plane's E2B compatibility hands it out as `envdAccessToken`), with neither `scopes` nor `expires_in_seconds` (400 `invalid_field`, `param` `e2b`): `url`, the same token every time it is asked for, as E2B's is one for a sandbox's life, and good until the sandbox's tokens are revoked (its `expires_at` is 2100-01-01). The preview listener checks the addresses of files E2B's SDKs sign with it (`download_url`, `upload_url`: `x-preview-ingress`). A revoke of every token of the sandbox makes it another one from then on."
          }
        },
        "additionalProperties": false
      },
      "SandboxToken": {
        "type": "object",
        "description": "A short-lived credential for one sandbox.",
        "required": [
          "token",
          "sandbox_id",
          "scopes",
          "expires_at"
        ],
        "properties": {
          "token": {
            "type": "string",
            "pattern": "^st_[A-Za-z0-9_\\-]+\\.[A-Za-z0-9_\\-]+$",
            "description": "`st_<payload>.<signature>`. The only time it is shown; it cannot be listed, and it runs out. `POST /v1/sandboxes/{id}/tokens/revoke` revokes it alone (named by it, or by `id`), or with every other token of the sandbox signed before."
          },
          "id": {
            "type": "string",
            "pattern": "^tok_[0-9a-z]{20}$",
            "description": "The token's id: a digest of it, which gives nothing of it away. What `POST /v1/sandboxes/{id}/tokens/revoke` takes as `token_id` to revoke this token alone. (A node from before ids does not say it.)"
          },
          "sandbox_id": {
            "type": "string",
            "pattern": "^sbx-[0-9a-hjkmnp-tv-z]{16}$"
          },
          "scopes": {
            "type": "array",
            "items": {
              "type": "string",
              "x-extensible-enum": [
                "url",
                "exec",
                "files",
                "tty"
              ]
            }
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "TokenRevokeRequest": {
        "type": "object",
        "description": "No body, or `{}`: every token of the sandbox signed so far is revoked. One of `token` and `token_id`: that token alone.",
        "properties": {
          "token": {
            "type": "string",
            "pattern": "^st_[A-Za-z0-9_\\-]+\\.[A-Za-z0-9_\\-]+$",
            "description": "The token to revoke, as `POST /v1/sandboxes/{id}/tokens` gave it: one of this sandbox's, run out or not (another is 400 `invalid_field`)."
          },
          "token_id": {
            "type": "string",
            "pattern": "^tok_[0-9a-z]{20}$",
            "description": "The id of the token to revoke, as `POST /v1/sandboxes/{id}/tokens` answered it (`id`)."
          }
        },
        "additionalProperties": false
      },
      "TokenRevocation": {
        "type": "object",
        "description": "A revoke of a sandbox's tokens. Of every token: every token signed for it before is refused from now on, and tokens are signed at `generation` from now on. Of one (`token_id`): that token is refused from now on, and the revoke is kept until `expires_at`, when the token runs out; the generation does not move, and is not said.",
        "required": [
          "object",
          "sandbox_id",
          "revoked_at"
        ],
        "properties": {
          "object": {
            "const": "token_revocation"
          },
          "sandbox_id": {
            "type": "string",
            "pattern": "^sbx-[0-9a-hjkmnp-tv-z]{16}$"
          },
          "generation": {
            "type": "integer",
            "format": "int64",
            "minimum": 1,
            "description": "Of a revoke of every token: the generation of the sandbox's tokens from now on, one more with each such revoke. A token signed at another one is refused."
          },
          "token_id": {
            "type": "string",
            "pattern": "^tok_[0-9a-z]{20}$",
            "description": "Of a revoke of one token: its id."
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "description": "Of a revoke of one token: when the token runs out (named by its id alone, the latest it may), until when its revoke is kept."
          },
          "revoked_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "additionalProperties": false
      },
      "ForwardEof": {
        "type": "object",
        "description": "Client to server, as a text message: the client sends no more. The guest half-closes its connection to the peer; what the peer still sends comes back.",
        "required": [
          "type"
        ],
        "properties": {
          "type": {
            "type": "string",
            "const": "eof"
          }
        }
      },
      "AgentListening": {
        "type": "object",
        "description": "Server to client, the first message: where the socket is in the sandbox.",
        "required": [
          "type",
          "path"
        ],
        "properties": {
          "type": {
            "type": "string",
            "const": "listening"
          },
          "path": {
            "type": "string",
            "description": "`/run/sbx/ssh-agent.<id>/agent`."
          }
        }
      },
      "AgentReset": {
        "type": "object",
        "description": "Server to client: every connection to the socket is gone (the sandbox went to sleep); the socket stays.",
        "required": [
          "type"
        ],
        "properties": {
          "type": {
            "type": "string",
            "const": "reset"
          }
        }
      },
      "InboundPortList": {
        "type": "object",
        "required": [
          "data",
          "next_cursor"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/InboundPortShown"
            }
          },
          "next_cursor": {
            "type": "null",
            "description": "Always null: the list is whole."
          }
        }
      },
      "InboundPortShown": {
        "type": "object",
        "required": [
          "object",
          "sandbox",
          "protocol",
          "port",
          "from",
          "address"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "inbound_port"
          },
          "sandbox": {
            "type": "string",
            "description": "The sandbox's id."
          },
          "protocol": {
            "type": "string",
            "enum": [
              "tcp",
              "udp"
            ]
          },
          "port": {
            "type": "integer",
            "minimum": 1,
            "maximum": 65535
          },
          "from": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "address": {
            "type": [
              "string",
              "null"
            ],
            "description": "Where it is reached: `[<ipv6>]:<port>`."
          }
        }
      },
      "InboundPortOpen": {
        "type": "object",
        "required": [
          "from"
        ],
        "properties": {
          "from": {
            "type": "array",
            "minItems": 1,
            "maxItems": 32,
            "items": {
              "type": "string"
            },
            "description": "The IPv6 prefixes the port is open to, and nobody else: `2001:db8::/32`, a single address (`2001:db8::7`), `::/0` for everybody. Nothing may be set past a prefix's length."
          }
        }
      },
      "InboundPortClosed": {
        "type": "object",
        "required": [
          "object",
          "sandbox",
          "protocol",
          "port",
          "closed",
          "was_open"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "inbound_port"
          },
          "sandbox": {
            "type": "string"
          },
          "protocol": {
            "type": "string",
            "enum": [
              "tcp",
              "udp"
            ]
          },
          "port": {
            "type": "integer",
            "minimum": 1,
            "maximum": 65535
          },
          "closed": {
            "type": "boolean",
            "const": true
          },
          "was_open": {
            "type": "boolean",
            "description": "Whether the port was open before the call."
          }
        }
      },
      "PortUrl": {
        "type": "object",
        "required": [
          "port",
          "url",
          "public"
        ],
        "properties": {
          "port": {
            "$ref": "#/components/schemas/Port"
          },
          "url": {
            "type": "string",
            "description": "The preview URL: the short form if the port is public, the signed form otherwise."
          },
          "public": {
            "type": "boolean",
            "description": "Whether the port is public: in `public_ports`, or not among `public_ports_except` where that is given."
          }
        }
      },
      "RunRequest": {
        "type": "object",
        "required": [
          "command"
        ],
        "properties": {
          "command": {
            "description": "A string is one line for `sh -c`; a list is run as it is, without a shell, its first word found as a shell finds it: along the `PATH` of the command's own environment (the image's, with what `env` says). At most 1000 arguments, no NUL bytes.",
            "oneOf": [
              {
                "type": "string",
                "minLength": 1
              },
              {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "minItems": 1,
                "maxItems": 1000
              }
            ]
          },
          "cwd": {
            "type": "string",
            "description": "An absolute path to run in. Default: the image's working directory. A directory that does not exist is not an error of the call: the command ends with exit code 126."
          },
          "env": {
            "$ref": "#/components/schemas/Env",
            "description": "Added to the sandbox's `env` for this command; the same name wins here."
          },
          "user": {
            "type": "string",
            "minLength": 1,
            "maxLength": 64,
            "description": "A user name, a uid, or `uid:gid`. Default: the image's user."
          },
          "timeout_seconds": {
            "type": "number",
            "exclusiveMinimum": 0,
            "maximum": 1000000000,
            "description": "When this is up the whole process group gets TERM, and KILL 10 seconds later; the result's `status` is `timed_out`. Left out: no limit."
          },
          "stdin": {
            "type": "string",
            "description": "Fed to the command's input, which is then closed (unless `stdin_open`). Base64 when `encoding` says so. The whole request is 64 MiB at the most (413 `body_too_large` beyond): a longer input goes in `…/exec/{exec_id}/stdin` calls, or in a file first."
          },
          "encoding": {
            "type": "string",
            "enum": [
              "utf8",
              "base64"
            ],
            "default": "utf8",
            "description": "`base64`: `stdin` is base64, and `stdout` and `stderr` come back as base64 (in a stream: events carry `base64` instead of `text`). For output that is not text."
          },
          "stdin_open": {
            "type": "boolean",
            "default": false,
            "description": "Keep the command's input open after `stdin`. Accepted here, though only `…/stdin` can then close it."
          },
          "wait_seconds": {
            "type": "number",
            "minimum": 0,
            "maximum": 3600,
            "default": 30,
            "description": "How long the call waits for the command to end before it answers 202."
          },
          "stream": {
            "type": "boolean",
            "description": "Answer with an event stream instead of one JSON object. Default: true if the request's `Accept` contains `text/event-stream`, false otherwise."
          },
          "wait_for_reader": {
            "type": "boolean",
            "default": false,
            "description": "Have the command wait for whoever reads this call's answer, instead of running ahead: once 2 MiB of its output are still to be read it writes no more, as on a full pipe, until they are taken. A stream then carries all of the output, however much there is and however slowly it is read, and never a `gap`. Without it a command never waits, and a reader that falls behind what the sandbox keeps (16 MiB) is told what it missed. When the reader has gone the command waits 10 seconds for another and then runs on. A sandbox made before the node knew this field pays no attention to it."
          }
        },
        "additionalProperties": false,
        "x-refused-fields": {
          "tty": "terminal_needs_exec"
        }
      },
      "ExecList": {
        "type": "object",
        "description": "One page of commands, newest first.",
        "required": [
          "data",
          "next_cursor"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ExecSummary"
            }
          },
          "next_cursor": {
            "$ref": "#/components/schemas/Cursor"
          }
        }
      },
      "ExecSummary": {
        "type": "object",
        "description": "A command as a list shows it: what the node noted, without waking the sandbox.",
        "required": [
          "id",
          "object",
          "sandbox_id",
          "command",
          "tty",
          "status",
          "exit_code",
          "signal",
          "started_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^exe-[0-9a-hjkmnp-tv-z]{16}$",
            "description": "`exe-` and 16 characters. With an `Idempotency-Key` it is derived from the sandbox's id, the call (`run` or `exec`) and the key."
          },
          "object": {
            "type": "string",
            "const": "exec"
          },
          "sandbox_id": {
            "type": "string",
            "pattern": "^sbx-[0-9a-hjkmnp-tv-z]{16}$"
          },
          "command": {
            "type": "string",
            "description": "The command as text: the line, or the arguments joined by spaces; `shell` for a terminal started without a command. At most 1024 characters. A command that came with a snapshot or a fork is listed as it was where it started: the sandbox keeps this itself."
          },
          "tty": {
            "type": "boolean",
            "description": "Whether it runs on a terminal."
          },
          "status": {
            "type": "string",
            "x-extensible-enum": [
              "running",
              "exited",
              "timed_out",
              "killed"
            ],
            "description": "`exited`: it ended by itself, with any exit code. `timed_out`: `timeout_seconds` was up. `killed`: a signal ended it."
          },
          "exit_code": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Null while running. 128 plus the signal's number when a signal ended it. A non-zero code is a result, not an error."
          },
          "signal": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^SIG[A-Z0-9]+$",
            "description": "The signal that ended it, as `SIGTERM`, `SIGKILL`, … or `SIG<number>` for one the daemon has no name for; null otherwise."
          },
          "started_at": {
            "type": "string",
            "format": "date-time",
            "description": "From the sandbox's clock: the same in the full object and in a list."
          }
        }
      },
      "ExecRequest": {
        "type": "object",
        "description": "`command` may be left out only together with `tty`: the command is then a shell (bash if the image has it, sh otherwise).",
        "properties": {
          "command": {
            "description": "A string is one line for `sh -c`; a list is run as it is, without a shell, its first word found as a shell finds it: along the `PATH` of the command's own environment (the image's, with what `env` says). At most 1000 arguments, no NUL bytes.",
            "oneOf": [
              {
                "type": "string",
                "minLength": 1
              },
              {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "minItems": 1,
                "maxItems": 1000
              }
            ]
          },
          "cwd": {
            "type": "string",
            "description": "An absolute path to run in. Default: the image's working directory. A directory that does not exist is not an error of the call: the command ends with exit code 126."
          },
          "env": {
            "$ref": "#/components/schemas/Env",
            "description": "Added to the sandbox's `env` for this command; the same name wins here."
          },
          "user": {
            "type": "string",
            "minLength": 1,
            "maxLength": 64,
            "description": "A user name, a uid, or `uid:gid`. Default: the image's user."
          },
          "timeout_seconds": {
            "type": "number",
            "exclusiveMinimum": 0,
            "maximum": 1000000000,
            "description": "When this is up the whole process group gets TERM, and KILL 10 seconds later; the result's `status` is `timed_out`. Left out: no limit."
          },
          "stdin": {
            "type": "string",
            "description": "Fed to the command's input, which is then closed (unless `stdin_open`). Base64 when `encoding` says so. The whole request is 64 MiB at the most (413 `body_too_large` beyond): a longer input goes in `…/exec/{exec_id}/stdin` calls, or in a file first."
          },
          "encoding": {
            "type": "string",
            "enum": [
              "utf8",
              "base64"
            ],
            "default": "utf8",
            "description": "`base64`: `stdin` is base64, and `stdout` and `stderr` come back as base64 (in a stream: events carry `base64` instead of `text`). For output that is not text."
          },
          "stdin_open": {
            "type": "boolean",
            "default": false,
            "description": "Keep the command's input open for `…/stdin`."
          },
          "tty": {
            "description": "Run on a pseudo-terminal: `true` for 80 by 24, or its size. A key other than `cols` and `rows` is refused (400 `unknown_field`, `param` `tty.<key>`).",
            "oneOf": [
              {
                "type": "boolean"
              },
              {
                "type": "object",
                "properties": {
                  "cols": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 2000,
                    "default": 80
                  },
                  "rows": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 2000,
                    "default": 24
                  }
                },
                "additionalProperties": false
              }
            ]
          }
        },
        "additionalProperties": false,
        "x-refused-fields": {
          "wait_seconds": "exec_does_not_wait",
          "stream": "exec_does_not_wait",
          "wait_for_reader": "exec_does_not_wait"
        }
      },
      "StdinRequest": {
        "type": "object",
        "properties": {
          "data": {
            "type": "string",
            "description": "The input: text, or base64 when `encoding` says so."
          },
          "encoding": {
            "type": "string",
            "enum": [
              "utf8",
              "base64"
            ],
            "default": "utf8"
          },
          "eof": {
            "type": "boolean",
            "default": false,
            "description": "Close the input after `data`."
          }
        },
        "additionalProperties": false
      },
      "SignalRequest": {
        "type": "object",
        "properties": {
          "signal": {
            "description": "A name (HUP, INT, QUIT, ILL, TRAP, ABRT, BUS, FPE, KILL, USR1, SEGV, USR2, PIPE, ALRM, TERM, STKFLT, CHLD, CONT, STOP, TSTP, TTIN, TTOU, URG, XCPU, XFSZ, VTALRM, PROF, WINCH, IO, PWR, SYS; any case, with or without `SIG`; or `SIG<number>`, as a result writes a signal that has no name) or a number from 1 to 64. The names are the ones results report.",
            "default": "TERM",
            "oneOf": [
              {
                "type": "string",
                "pattern": "^[A-Za-z0-9]{1,16}$"
              },
              {
                "type": "integer",
                "minimum": 1,
                "maximum": 64
              }
            ]
          }
        },
        "additionalProperties": false
      },
      "TerminalResize": {
        "type": "object",
        "description": "Client to server, as a text message: the terminal has this size now. Out-of-range sizes are dropped without an answer.",
        "required": [
          "type",
          "cols",
          "rows"
        ],
        "properties": {
          "type": {
            "type": "string",
            "const": "resize"
          },
          "cols": {
            "type": "integer",
            "minimum": 1,
            "maximum": 2000
          },
          "rows": {
            "type": "integer",
            "minimum": 1,
            "maximum": 2000
          }
        }
      },
      "TerminalSignal": {
        "type": "object",
        "description": "Client to server, as a text message: send the command's process group a signal.",
        "required": [
          "type"
        ],
        "properties": {
          "type": {
            "type": "string",
            "const": "signal"
          },
          "signal": {
            "description": "As in `SignalRequest`: a name or a number. One that is no signal is dropped without an answer.",
            "default": "INT",
            "oneOf": [
              {
                "type": "string",
                "pattern": "^[A-Za-z0-9]{1,16}$"
              },
              {
                "type": "integer",
                "minimum": 1,
                "maximum": 64
              }
            ]
          }
        }
      },
      "TerminalExit": {
        "type": "object",
        "description": "Server to client, as a text message: the command has ended. A close frame (1000) follows.",
        "required": [
          "type",
          "status",
          "exit_code",
          "signal"
        ],
        "properties": {
          "type": {
            "type": "string",
            "const": "exit"
          },
          "status": {
            "type": "string",
            "x-extensible-enum": [
              "exited",
              "timed_out",
              "killed"
            ]
          },
          "exit_code": {
            "type": "integer"
          },
          "signal": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^SIG[A-Z0-9]+$"
          }
        }
      },
      "TerminalError": {
        "type": "object",
        "description": "Server to client, as a text message: the command was lost (the sandbox was restarted, restored or deleted), or the sandbox was moved to another node with its processes, where the command goes on (`sandbox_moved`, retryable, with `to`, `epoch` and `offset`: join again through the control plane with `from` set to `offset`), or a key or a message of the client's was refused (`spend_limit_reached`, say). `error` is the error object of every other error. A close frame follows: 1012 with the reason `sandbox_moved` after `sandbox_moved`, 1000 otherwise. A key typed while the sandbox is being moved waits for the move's end: it goes in where the move failed.",
        "required": [
          "type",
          "error"
        ],
        "properties": {
          "type": {
            "type": "string",
            "const": "error"
          },
          "error": {
            "$ref": "#/components/schemas/Error"
          },
          "to": {
            "type": [
              "string",
              "null"
            ],
            "description": "With `sandbox_moved`: the node the sandbox was released to, as the control plane named it, or null."
          },
          "epoch": {
            "type": "integer",
            "format": "int64",
            "minimum": 1,
            "description": "With `sandbox_moved`: the epoch the sandbox was released at."
          },
          "offset": {
            "type": "integer",
            "format": "int64",
            "minimum": 0,
            "description": "With `sandbox_moved`: the position in the command's output the terminal had shown up to: `from` for joining again."
          }
        }
      },
      "TerminalGap": {
        "type": "object",
        "description": "Server to client, as a text message: the client fell further behind than the sandbox keeps of a command's output (the last 16 MiB), and what the terminal shows next begins at `available_from`, not where it stood. Sent before what follows it, as a stream's `gap` event is. The bytes a terminal shows carry no position: a client that counts them, to join again with `from` after its connection was lost (the SSH gateway, whose sessions outlive a restart of the node's daemon), goes on counting from `available_from`.",
        "required": [
          "type",
          "from",
          "available_from"
        ],
        "properties": {
          "type": {
            "type": "string",
            "const": "gap"
          },
          "from": {
            "type": "integer",
            "format": "int64",
            "minimum": 0,
            "description": "The position the terminal had shown up to."
          },
          "available_from": {
            "type": "integer",
            "format": "int64",
            "minimum": 0,
            "description": "The position what it shows next begins at."
          }
        }
      },
      "FileAttributes": {
        "type": "object",
        "description": "What to set of a file. Each field may be left out.",
        "properties": {
          "size_bytes": {
            "type": "integer",
            "format": "int64",
            "minimum": 0,
            "description": "The file's length: what lies past it is cut off, and a shorter file is extended with zeros."
          },
          "mode": {
            "type": "string",
            "pattern": "^[0-7]{1,4}$",
            "description": "Permission bits as octal digits."
          },
          "modified_at": {
            "type": "string",
            "format": "date-time",
            "description": "The time of the last modification, to the second."
          }
        },
        "additionalProperties": false
      },
      "FileEntryList": {
        "type": "object",
        "description": "One page of a directory's entries, by name.",
        "required": [
          "data",
          "next_cursor"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FileEntry"
            },
            "description": "The entries of this page, sorted by name."
          },
          "next_cursor": {
            "$ref": "#/components/schemas/Cursor"
          }
        }
      },
      "FileEntry": {
        "type": "object",
        "description": "A file, a directory or another entry of a sandbox's file system.",
        "required": [
          "name",
          "path",
          "type",
          "size_bytes",
          "mode",
          "modified_at"
        ],
        "properties": {
          "name": {
            "type": "string",
            "description": "The last part of the path; empty for `/`."
          },
          "path": {
            "type": "string",
            "description": "In a list: the directory's path, a slash and the name. In a stat: the path as it was given."
          },
          "type": {
            "type": "string",
            "x-extensible-enum": [
              "file",
              "directory",
              "symlink",
              "other"
            ],
            "description": "A symbolic link is reported as itself, not as what it points to, unless a stat was asked to follow it."
          },
          "size_bytes": {
            "type": "integer",
            "format": "int64",
            "minimum": 0
          },
          "mode": {
            "type": "string",
            "pattern": "^[0-7]{4}$",
            "description": "Permission bits as four octal digits."
          },
          "modified_at": {
            "type": "string",
            "format": "date-time",
            "description": "Whole seconds."
          }
        }
      },
      "MkdirRequest": {
        "type": "object",
        "required": [
          "path"
        ],
        "properties": {
          "path": {
            "type": "string",
            "description": "An absolute path."
          },
          "user": {
            "type": "string",
            "minLength": 1,
            "maxLength": 64,
            "description": "As for commands."
          }
        },
        "additionalProperties": false
      },
      "MoveRequest": {
        "type": "object",
        "required": [
          "from",
          "to"
        ],
        "properties": {
          "from": {
            "type": "string",
            "description": "An absolute path."
          },
          "to": {
            "type": "string",
            "description": "An absolute path. A file there is replaced."
          },
          "user": {
            "type": "string",
            "minLength": 1,
            "maxLength": 64
          }
        },
        "additionalProperties": false
      },
      "SnapshotList": {
        "type": "object",
        "description": "One page of snapshots, newest first.",
        "required": [
          "data",
          "next_cursor"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Snapshot"
            }
          },
          "next_cursor": {
            "$ref": "#/components/schemas/Cursor"
          }
        }
      },
      "SnapshotUpdate": {
        "type": "object",
        "properties": {
          "name": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/Name"
              },
              {
                "type": "null"
              }
            ],
            "description": "Null removes the name."
          },
          "expires_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Null: never."
          }
        },
        "additionalProperties": false
      },
      "SecretList": {
        "type": "object",
        "description": "One page of secrets, by name.",
        "required": [
          "data",
          "next_cursor"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Secret"
            }
          },
          "next_cursor": {
            "$ref": "#/components/schemas/Cursor"
          }
        }
      },
      "Secret": {
        "type": "object",
        "description": "A secret, as much as the API shows of one: its name, the hosts it may go to, and when it was made and last changed. Its value is never part of an answer.",
        "required": [
          "name",
          "object",
          "allowed_hosts",
          "created_at",
          "updated_at"
        ],
        "properties": {
          "name": {
            "type": "string",
            "pattern": "^[A-Za-z_][A-Za-z0-9_]{0,127}$"
          },
          "object": {
            "type": "string",
            "const": "secret"
          },
          "allowed_hosts": {
            "$ref": "#/components/schemas/SecretHosts"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "description": "When its value or its hosts were last put."
          }
        }
      },
      "SecretPut": {
        "type": "object",
        "required": [
          "value"
        ],
        "properties": {
          "value": {
            "type": "string",
            "minLength": 1,
            "maxLength": 65536,
            "description": "The secret itself: 1 to 65536 bytes, without line breaks or other control characters (a tab is fine), because it is written into a header as it is."
          },
          "allowed_hosts": {
            "$ref": "#/components/schemas/SecretHosts",
            "description": "The hosts it may be sent to; null for none. Not given: a secret that is there keeps the hosts it has, and one that is new has none, and goes nowhere."
          }
        },
        "additionalProperties": false
      },
      "SecretPatch": {
        "type": "object",
        "required": [
          "allowed_hosts"
        ],
        "properties": {
          "allowed_hosts": {
            "$ref": "#/components/schemas/SecretHosts",
            "description": "The hosts it may be sent to from now on, in place of those it had; null for none."
          }
        },
        "additionalProperties": false
      }
    },
    "responses": {
      "ConsoleCsrf": {
        "description": "`csrf_header_missing`: no `X-Requested-By: console`; `csrf_origin_mismatch`: an `Origin` that is not the console's. Every call under `/v1/auth/` carries both (`consoleSession` says why).",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        },
        "x-codes": [
          "csrf_header_missing",
          "csrf_origin_mismatch"
        ]
      },
      "NoSession": {
        "description": "`missing_api_key`: neither the session's cookie nor a key; `invalid_session`: the cookie's session has ended (signed out, ended by its user, by a new password, by an operator's lock), ran out (thirty days unused), or never was: sign in again; `invalid_api_key`, `expired_api_key`, `invalid_token`: a key or a sandbox token that is none or has run out (a key that is one is 403 `needs_session`).",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        },
        "x-codes": [
          "missing_api_key",
          "invalid_session",
          "invalid_api_key",
          "expired_api_key",
          "invalid_token"
        ]
      },
      "SessionOnly": {
        "description": "`needs_session`: a key (a project's, the operator's), which opens none of the console's own calls: they are a signed-in user's. (A sandbox token is passed to its node, which answers 404 `no_such_route`.)",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        },
        "x-codes": [
          "needs_session"
        ]
      },
      "SessionChange": {
        "description": "`needs_session`: a key (a project's, the operator's), which opens none of the console's own calls; `csrf_header_missing`: no `X-Requested-By: console`; `csrf_origin_mismatch`: an `Origin` that is not the console's (`consoleSession`).",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        },
        "x-codes": [
          "needs_session",
          "csrf_header_missing",
          "csrf_origin_mismatch"
        ]
      },
      "AccountInDebt": {
        "description": "`account_in_debt`: the account owes (its balance is below zero), and until it has paid nothing of it can be used: every call of its keys, and of the tokens of its sandboxes, is refused so, but those that read what it owes and pay it; those that list and read the backups of its volumes and make a volume from one (`POST /v1/volumes` with `from_backup`, but while what it stores is being deleted), what one that stopped paying wants most; and, unless the control plane is started with `--debt-allows-delete false`, those that list, read and delete the volumes, snapshots and sandboxes it stores, and delete its backups. Never a new backup. Before the key's scopes are looked at, and whatever the path. The message says how much is owed, since when, when what the account stores is deleted, how to pay, that it can delete what it stores to stop the amount from growing, and that a volume is still made from a backup (`GET /v1/billing`, `debt`).",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        },
        "x-codes": [
          "account_in_debt"
        ]
      },
      "Unauthenticated": {
        "description": "`missing_api_key`, `invalid_api_key`, `expired_api_key`, `invalid_token`.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        },
        "x-codes": [
          "missing_api_key",
          "invalid_api_key",
          "expired_api_key",
          "invalid_token"
        ]
      },
      "NotAllowed": {
        "description": "`key_scope`: the key does not allow the scope the call takes (the message names it); `needs_api_key`: the operator's key, or the SSH gateway's; `account_held`: the account is held for its payments, and every call of its keys but `GET /v1/account` is refused so, before the scopes are looked at (`x-error-codes`); `installation_key_scope`: a key held to an installation of a Managed Application, on a call that is not about that installation's own things. (A sandbox token is passed to its node, which answers what it has no call for with 404 `no_such_route`.)",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        },
        "x-codes": [
          "key_scope",
          "needs_api_key",
          "account_held",
          "installation_key_scope"
        ]
      },
      "NoSuchDomain": {
        "description": "`domain_not_found`: this project has no such registration. Another project's registration is not there for it.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        },
        "x-codes": [
          "domain_not_found"
        ]
      },
      "TrustLimitTaxRegionOrPlan": {
        "description": "`key_scope`: the key does not allow the scope the call takes (the message names it); `needs_api_key`: the operator's key, or the SSH gateway's; `account_held`: the account is held for its payments, and every call of its keys but `GET /v1/account` is refused so, before the scopes are looked at; `trust_limit_exceeded`: more than the account's trust level lets it have (`GET /v1/billing`, `trust`): a spend limit, or no limit, above what auto reload may take in a month at its level (`param` `spend_limit_usd`), an auto reload above it (`reload_amount_usd`), a top-up that would bring what its top-ups and auto reloads of the calendar month come to above what the level lets them (`amount_usd`). The message says the level, its two amounts, what of the month's is used, and what raises it; `tax_region_business_only`: the account is no business (it has no tax id that is verified, or being verified) and is in a place the platform sells only to businesses (its billing address, or, where it has none, the country of the address the call comes from: `GET /v1/account`, `tax`); the message names the place, and says how a business sets its tax id, which lets it pay at once (`x-error-codes`).",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        },
        "x-codes": [
          "key_scope",
          "needs_api_key",
          "account_held",
          "trust_limit_exceeded",
          "tax_region_business_only"
        ]
      },
      "AppRefused": {
        "description": "`invalid_json`, `unknown_field`, `unknown_parameter`; `invalid_field`: a field that is not what its description says (`param` names it, down to the part: `scale.concurrency`, `ready.http`, `files[0].path`), or a secret that `image_pull_secret` names and that holds no user name and password (nothing of what it holds is said); `invalid_name`; `invalid_image`: not an image reference; `invalid_env`: a name no environment variable can have, more than 256 KiB, a NUL byte; `invalid_reference`: a `${{` of `env` that begins no reference, or, when a version is made, a reference that goes round or refers to a service or a variable that is not there, or to a secret in other text (`param` is `env.<variable>`); `invalid_metadata`; `invalid_egress`; `unknown_region`; `invalid_idempotency_key`; `volume_single_instance`: a service with a volume of its own (one that is not shared) has one instance, which has the volume, and asks for more: `scale.max_instances` other than 1, `scale.min_instances` above 1, or `keys` (`param` names which).",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        },
        "x-codes": [
          "invalid_json",
          "unknown_field",
          "unknown_parameter",
          "invalid_field",
          "invalid_name",
          "invalid_image",
          "invalid_env",
          "invalid_reference",
          "invalid_metadata",
          "invalid_egress",
          "unknown_region",
          "invalid_idempotency_key",
          "volume_single_instance"
        ]
      },
      "AppNotAllowed": {
        "description": "`key_scope`: the key does not allow `write`, or the call gives the app a secret it does not have and the key does not allow `admin`; `needs_api_key`; `app_limit_reached`: the account has as many apps as its plan lets it have; `env_limit_reached`: more environment variables and secrets together than the plan lets an app have; `secret_limit_reached`: the version the call would make gives its instances more of the project's secrets, as environment variables and files together, than the plan lets one sandbox be given (`max_sandbox_secrets_bytes` of `GET /v1/billing`), and more than they are given now (`param` is the place at which they pass it: `secrets.<VARIABLE>` or `files[n].secret`; of a project's deploy, `services.<name>.` before it); the message names the limit; `plan_size_exceeded`: an instance larger than the plan's largest sandbox (`param` is `size`); `plan_cap_reached`: `scale.max_instances` or `scale.min_instances` given as more than the plan lets an app ask for in a region (`param` is `app_instances`; the message has the plan's number), or as many apps and versions made in the last hour as the account may make (`param` is `app_creates_per_hour`; `retryable`, with `Retry-After`); `spend_limit_reached`, `balance_used_up`: a call that would make a version (a create with an `image`, a change of what a version is made of, a deploy) in a project that is held back, refused with the code and the words a create of a sandbox gets there, before anything is built; one that makes no version is taken, and so is the same call once the project is let go of; `account_held`: the account is held for its payments, and every call of its keys but `GET /v1/account` is refused so, before the scopes are looked at (`x-error-codes`); `secret_host_not_allowed`: the app's egress rules as the call would leave them send a secret by `inject` to a host it may not go to (`param` is `egress.inject[n].host`; looked at where the call gives rules or makes a version), or the version it would make pulls its image with a secret that may not go to the image's registry (`param` is `image_pull_secret`); beside `error`, `allowed_hosts` says where the secret may go.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        },
        "x-codes": [
          "key_scope",
          "needs_api_key",
          "app_limit_reached",
          "env_limit_reached",
          "secret_limit_reached",
          "plan_size_exceeded",
          "plan_cap_reached",
          "spend_limit_reached",
          "balance_used_up",
          "account_held",
          "secret_host_not_allowed"
        ]
      },
      "TrustLimit": {
        "description": "`key_scope`: the key does not allow the scope the call takes (the message names it); `needs_api_key`: the operator's key, or the SSH gateway's; `account_held`: the account is held for its payments, and every call of its keys but `GET /v1/account` is refused so, before the scopes are looked at; `trust_limit_exceeded`: more than the account's trust level lets it have (`GET /v1/billing`, `trust`): a spend limit, or no limit, above what auto reload may take in a month at its level (`param` `spend_limit_usd`), an auto reload above it (`reload_amount_usd`), a top-up that would bring what its top-ups and auto reloads of the calendar month come to above what the level lets them (`amount_usd`). The message says the level, its two amounts, what of the month's is used, and what raises it (`x-error-codes`).",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        },
        "x-codes": [
          "key_scope",
          "needs_api_key",
          "account_held",
          "trust_limit_exceeded"
        ]
      },
      "NoSuchApp": {
        "description": "`app_not_found`: this project has no such app. Another project's app is not there for it.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        },
        "x-codes": [
          "app_not_found"
        ]
      },
      "TrustLimitOrTaxRegion": {
        "description": "`key_scope`: the key does not allow the scope the call takes (the message names it); `needs_api_key`: the operator's key, or the SSH gateway's; `account_held`: the account is held for its payments, and every call of its keys but `GET /v1/account` is refused so, before the scopes are looked at; `trust_limit_exceeded`: more than the account's trust level lets it have (`GET /v1/billing`, `trust`): a spend limit, or no limit, above what auto reload may take in a month at its level (`param` `spend_limit_usd`), an auto reload above it (`reload_amount_usd`), a top-up that would bring what its top-ups and auto reloads of the calendar month come to above what the level lets them (`amount_usd`). The message says the level, its two amounts, what of the month's is used, and what raises it; `tax_region_business_only`: the account is no business (it has no tax id that is verified, or being verified) and is in a place the platform sells only to businesses (its billing address, or, where it has none, the country of the address the call comes from: `GET /v1/account`, `tax`); the message names the place, and says how a business sets its tax id, which lets it pay at once (`x-error-codes`).",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        },
        "x-codes": [
          "key_scope",
          "needs_api_key",
          "account_held",
          "trust_limit_exceeded",
          "tax_region_business_only"
        ]
      },
      "Failure": {
        "description": "Any other failure, in the same shape. `internal` (500): the node failed; the message says no more, and the node's log has the reason under the request's id. `unavailable` (503, retryable): `no_capacity`, every network slot of the node is in use; `memory_full`, a machine the call has to start (made, woken, booted from its disk, resumed, rolled back, built) does not fit in the host's memory beside what the machines that run on it are promised, and nothing paused or idle could be put to sleep to make room for it (`memory` of the node); `drive_full`, the call would have a template built and the node's data drive has no room for one (`drive` of the node); `unavailable`, the node is shutting down, the sandbox was still `starting` after 10 minutes, or it did not answer, or it is being moved to this node (with `Retry-After: 1`); `fenced`, the node's lease fences the sandbox (`Node.lease`). `moved` (409): the sandbox was moved to another node, which `to` names.",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/X-Request-Id"
          },
          "Server-Timing": {
            "$ref": "#/components/headers/Server-Timing"
          },
          "Retry-After": {
            "$ref": "#/components/headers/Retry-After"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/NodeErrorResponse"
            }
          }
        },
        "x-codes": [
          "internal",
          "no_capacity",
          "memory_full",
          "drive_full",
          "unavailable",
          "fenced",
          "moved"
        ]
      }
    },
    "parameters": {
      "Limit": {
        "name": "limit",
        "in": "query",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "maximum": 1000,
          "default": 100
        }
      },
      "Cursor": {
        "name": "cursor",
        "in": "query",
        "description": "`next_cursor` of the page before.",
        "schema": {
          "type": "string"
        }
      },
      "IdempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "description": "1 to 255 characters: the same key and body again gets the same answer, and nothing is made twice.",
        "schema": {
          "type": "string",
          "minLength": 1,
          "maxLength": 255
        }
      },
      "App": {
        "name": "app",
        "in": "path",
        "required": true,
        "description": "The app's id (`app-...`) or its name.",
        "schema": {
          "type": "string"
        }
      },
      "Key": {
        "name": "key",
        "in": "path",
        "required": true,
        "description": "A key of the app: 1 to 128 characters, none of them a control character, written into the path as a segment is (`%XX` for a byte that is not a letter, a digit or one of `-._~`: `agent%2042` for `agent 42`).",
        "schema": {
          "type": "string"
        }
      },
      "InstancePortNumber": {
        "name": "port",
        "in": "path",
        "required": true,
        "description": "A port, from 1 to 65535.",
        "schema": {
          "type": "string"
        }
      },
      "KeySecret": {
        "name": "secret",
        "in": "path",
        "required": true,
        "description": "The id of one of the app's key secrets (`ksec-...`).",
        "schema": {
          "type": "string"
        }
      },
      "NodeIdempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "description": "Makes the request safe to send again. See \"Idempotency-Key\" in the introduction.",
        "schema": {
          "type": "string",
          "minLength": 1,
          "maxLength": 255
        }
      },
      "RequestId": {
        "name": "X-Request-Id",
        "in": "header",
        "description": "The caller's own id for the request: 1 to 64 letters, digits, `.`, `_` and `-`. The node then knows the request by it: in the answer's `X-Request-Id`, in error bodies and in its log. Anything else is ignored, and the node makes an id.",
        "schema": {
          "type": "string",
          "pattern": "^[A-Za-z0-9._\\-]{1,64}$"
        }
      },
      "NodeLimit": {
        "name": "limit",
        "in": "query",
        "description": "How many items a page holds.",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "maximum": 1000,
          "default": 100
        }
      },
      "NodeCursor": {
        "name": "cursor",
        "in": "query",
        "description": "The `next_cursor` of the page before.",
        "schema": {
          "type": "string"
        }
      },
      "SandboxRef": {
        "name": "id",
        "in": "path",
        "required": true,
        "description": "A sandbox's id (`sbx-…`) or its name.",
        "schema": {
          "type": "string"
        }
      },
      "InboundPortNumber": {
        "name": "port",
        "in": "path",
        "required": true,
        "description": "A port, from 1 to 65535.",
        "schema": {
          "type": "string"
        }
      },
      "ExecRef": {
        "name": "exec_id",
        "in": "path",
        "required": true,
        "description": "A command's id (`exe-…`).",
        "schema": {
          "type": "string"
        }
      },
      "FilePath": {
        "name": "path",
        "in": "query",
        "required": true,
        "description": "An absolute path in the sandbox, at most 4096 bytes.",
        "schema": {
          "type": "string"
        }
      },
      "FileUser": {
        "name": "user",
        "in": "query",
        "description": "Do it as this user: a name, a uid, or `uid:gid`. Default: the user commands run as.",
        "schema": {
          "type": "string",
          "minLength": 1,
          "maxLength": 64
        }
      },
      "SnapshotRef": {
        "name": "id",
        "in": "path",
        "required": true,
        "description": "A snapshot's id (`snap-…`) or its name.",
        "schema": {
          "type": "string"
        }
      },
      "SecretName": {
        "name": "name",
        "in": "path",
        "required": true,
        "description": "A secret's name: 1 to 128 letters, digits and underscores, not beginning with a digit (what `${secrets.NAME}` can name).",
        "schema": {
          "type": "string"
        }
      }
    },
    "headers": {
      "X-Request-Id": {
        "description": "The request's id: the `X-Request-Id` the caller sent, or one the node made (`req-` and 13 characters). It is also in error bodies and in the node's log.",
        "required": true,
        "schema": {
          "type": "string",
          "pattern": "^[A-Za-z0-9._\\-]{1,64}$"
        }
      },
      "Idempotent-Replayed": {
        "description": "`true` when the answer is the one an earlier request with the same `Idempotency-Key` got.",
        "schema": {
          "type": "string",
          "const": "true"
        }
      },
      "Server-Timing": {
        "description": "`wake;dur=<milliseconds>`: present on every answer of a call that had to wake the sandbox, boot it from its disk or wait for it to start, whether the answer is a result or a refusal; it says how long that took.",
        "schema": {
          "type": "string",
          "pattern": "^wake;dur=[0-9]+(\\.[0-9]+)?$"
        }
      },
      "Retry-After": {
        "description": "`1`: on a 503 of a sandbox that is being moved to this node, or of a release or takeover that could not be made now. The same call again in a second may pass.",
        "schema": {
          "type": "string",
          "const": "1"
        }
      },
      "Location": {
        "description": "The address of what was made: a path on this server.",
        "required": true,
        "schema": {
          "type": "string",
          "pattern": "^/v1/"
        }
      }
    },
    "securitySchemes": {
      "apiKey": {
        "type": "http",
        "scheme": "bearer",
        "description": "A project's key (`ak_...`)."
      },
      "sandboxToken": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "st_<payload>.<signature>",
        "description": "A sandbox token from `POST /v1/sandboxes/{id}/tokens`, for that sandbox only. The requirement lists the scope the operation needs; an empty list means any token for the sandbox will do."
      },
      "sandboxTokenInQuery": {
        "type": "apiKey",
        "in": "query",
        "name": "sandbox_token",
        "description": "The same token as a query parameter. `GET` only."
      },
      "consoleSession": {
        "type": "apiKey",
        "in": "cookie",
        "name": "__Host-console",
        "description": "A user signed in to the console (`POST /v1/auth/sign-in`): its session, in the cookie the sign-in set (`__Host-console`; `console` on a control plane started with `--console-insecure-cookies true`). The console's own calls (`/v1/me…`, `/v1/accounts/{id}…`) take it and no key: a key is 403 `needs_session`. Every other call of `/v1` that a project's key makes may be made with it too, as a key of that project with every scope would make it, with the same answers and refusals and the same entry in the project's audit log, whose `actor` is then `{type: \"session\", id: <the user's id>, name: <the user's address>}`: the call names its account (`X-Account: acct-…`, one the user is a member of) and, unless it is one of the account's own (`/v1/billing…`, `/v1/account`, made as the account's first project), its project (`X-Project: prj-…`, one of the account's). The user's role in the account is asked first: the account's billing (every call under `/v1/billing`), `PATCH /v1/account` and a key with `admin` among its scopes (`POST /v1/keys`) are its owners'; and a user whose address is not verified yet makes nothing (every call that is not a `GET`). A call that comes with a key (`Authorization`) is the key's, whatever cookie comes with it; a WebSocket is opened by no cookie (the console asks for a sandbox token of `tty` with the session, and opens the terminal with it in the URL, `?sandbox_token=`); a call passed on to a node carries neither the cookie nor `X-Account` and `X-Project`. Every call with the cookie that is not a `GET`, and every call under `/v1/auth/`, carries `X-Requested-By: console` and an `Origin` that is the console's: the origin of `--console-url`, or, without it, one whose host is the call's `Host`. A page of another site cannot send the header without the control plane's leave, which it does not give, and cannot change the `Origin` a browser sends; and the cookie (`SameSite=Lax`) is not sent with what such a page asks for in the first place. What a call of the API made with the session is refused with, beside what its operation lists, is `x-session-calls`."
      },
      "connectedApp": {
        "type": "oauth2",
        "description": "An app a user connected with OAuth (tag `Connected apps`): an access token of Glac's authorization server, Ory Hydra (`--connect-issuer`), as `Authorization: Bearer`. A JWT, checked against the authorization server's keys (RS256), its `iss` the authorization server, its `aud` naming the API (`--connect-api`), not expired (five seconds' leeway: a token lasts ten minutes, and the app refreshes it), of a connection that is not revoked (its id is the token's `ext.grant`), whose user is the token's `sub` and app its `client_id`, not locked and still a member of the account. Every call of `/v1` a project's key makes may be made with it, as a key of the project the connection covers (one project; of several, the one `X-Project` names; the account's own calls as the first) with the scopes the token's own give in the call's area: `sandboxes:read` (`read` in the area of sandboxes: `/v1/sandboxes`, `/v1/run`, `/v1/snapshots`, `/v1/volumes`, `/v1/drives`, `/v1/drive-snapshots`, `/v1/backups`, `/v1/ssh-keys`, `/v1/browsers`, and the node's other calls), `sandboxes:write` (there: `read`, `write`, `exec`, `files`, `tty`, `url`), `apps:read` (`read` in the area of apps: `/v1/apps`, `/v1/deploys`, `/v1/compose`, `/v1/variables`, `/v1/domains`, `/v1/destinations`, `/v1/databases`, and Managed Applications' `/v1/catalogue`, `/v1/installations`, `/v1/managed`), `apps:deploy` (there: `read`, `write`), `billing:read` (`read` of `/v1/billing…`, `/v1/usage`, `/v1/limits…`, `/v1/account`); the project (`/v1/project`, `/v1/events`, `/v1/secrets`) and `GET /v1/connection` are read with any of them. Never `admin`, and never the console's own calls. What the user's role in the account keeps from the console keeps from its apps (the account's billing is its owners'), and a user whose address is not verified makes nothing. The same answers and refusals as a key's, and the same entry in the project's audit log, whose `actor` is then `{type: \"connection\", id: <the connection's id>, name: \"<the app> for <the user's address>\"}`. What a call made with it is refused with, beside what its operation lists, is `x-connection-calls`; each such refusal carries `WWW-Authenticate` (RFC 6750) that names the API's protected resource metadata, and the scope that would let it.",
        "flows": {
          "authorizationCode": {
            "authorizationUrl": "https://auth.glac.ai/oauth2/auth",
            "tokenUrl": "https://auth.glac.ai/oauth2/token",
            "refreshUrl": "https://auth.glac.ai/oauth2/token",
            "scopes": {
              "sandboxes:read": "See sandboxes, their snapshots, Drives, backups and SSH keys, and Browsers sessions",
              "sandboxes:write": "Make, change and delete them; run commands, read and write files, open terminals and port links",
              "apps:read": "See apps on Cube, their versions, logs and metrics, deploys, domains, shared variables, databases and Managed Applications",
              "apps:deploy": "Deploy, roll back, scale and delete apps, deploys, shared variables and databases, and install Managed Applications",
              "billing:read": "See the account's plan, bill, Credits, usage and limits (an owner's grant)",
              "offline_access": "A refresh token",
              "openid": "An ID token"
            }
          }
        }
      }
    }
  }
}
