{
  "openapi": "3.1.0",
  "info": {
    "title": "Wheelmachine API",
    "version": "1.0.0",
    "summary": "Machines for agents and people, by the second.",
    "description": "One API for machines that start in milliseconds from ready spares, pause with their memory kept, copy while\nthey run, and keep their files sealed with your account's own key.\n\nEvery call carries an API key: `Authorization: Bearer ak_live_...`. A key holds only the scopes it was made\nwith, and keys made for other work (models) hold no machine scope at all.\n\nPeople sign in at the dashboard, https://app.wheelmachine.ai, with GitHub or Google, in the browser. The dashboard\ncalls these same routes on its own address with the person's sign-in instead of a key - a cookie for that address\nalone, and the sign-in's token in `X-CSRF-Token` on every call (on a WebSocket's handshake, which carries no header\nof a page's own, as the protocol `wm.csrf.<token>` beside `wm.terminal`) - and may do what a `cloud:*` key may. A program\ngets its key the same way, through the browser (`wm login`): it starts a sign-in, the person approves it at\nhttps://app.wheelmachine.ai/device, and the program is given a new key of the account, once.\n\nErrors are `application/problem+json` (RFC 9457) with a stable `code`. Every answer carries `X-Request-Id`;\ngive it when you ask us about a call. Calls that create something accept an `Idempotency-Key`, so a retry after\na dropped connection never makes two.\n"
  },
  "servers": [
    {
      "url": "https://api.wheelmachine.ai",
      "description": "Wheelmachine"
    },
    {
      "url": "https://app.wheelmachine.ai",
      "description": "The dashboard, where people sign in; it calls the same routes with their sign-in"
    }
  ],
  "security": [
    {
      "key": []
    },
    {
      "session": [],
      "csrf": []
    }
  ],
  "tags": [
    {
      "name": "Account"
    },
    {
      "name": "Sign-in"
    },
    {
      "name": "Projects"
    },
    {
      "name": "Keys"
    },
    {
      "name": "Machines"
    },
    {
      "name": "Files and commands"
    },
    {
      "name": "Ports",
      "description": "A program listening on a port inside a machine, at a web address of its own:\n`https://<port>-<the machine's id without m_>.run.wheelmachine.ai`. HTTP, WebSockets and streams pass\nthrough; a paused or archived machine is started by the first visit (a browser is shown a page that says so\nwhile it starts). An address is private unless the port was opened `public`: it answers a call that carries\nan API key able to see the machine (`Authorization: Bearer ak_live_...`, which never reaches the program) and\na browser that opened a link made for it; anyone else is answered 401, and an address of a port that is not\nopen, or of another account's machine, 404.\n"
    },
    {
      "name": "Secrets"
    },
    {
      "name": "Catalogue"
    }
  ],
  "paths": {
    "/v1/me": {
      "get": {
        "tags": [
          "Account"
        ],
        "operationId": "getMe",
        "summary": "The account and key this call is made with",
        "responses": {
          "200": {
            "description": "Who is calling",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Me"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/v1/projects": {
      "get": {
        "tags": [
          "Projects"
        ],
        "operationId": "listProjects",
        "summary": "The account's projects",
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/After"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of projects",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProjectList"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest",
            "description": "`limit` or `after` is not right"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      },
      "post": {
        "tags": [
          "Projects"
        ],
        "operationId": "createProject",
        "summary": "Make a project",
        "description": "Needs the `cloud:projects:write` scope. A key limited to some projects cannot make new ones.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ProjectCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The new project",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Project"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/BadRequest",
            "description": "This `Idempotency-Key` was used with a different request"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/v1/projects/{project_id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/ProjectId"
        }
      ],
      "get": {
        "tags": [
          "Projects"
        ],
        "operationId": "getProject",
        "summary": "One project",
        "responses": {
          "200": {
            "description": "The project",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Project"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "patch": {
        "tags": [
          "Projects"
        ],
        "operationId": "updateProject",
        "summary": "Rename a project",
        "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": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "delete": {
        "tags": [
          "Projects"
        ],
        "operationId": "deleteProject",
        "summary": "Delete an empty project",
        "description": "A project that still holds machines is refused with `409 project_not_empty`.",
        "responses": {
          "204": {
            "description": "Deleted"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        }
      }
    },
    "/v1/keys": {
      "get": {
        "tags": [
          "Keys"
        ],
        "operationId": "listKeys",
        "summary": "The account's machine keys",
        "description": "Keys that hold at least one `cloud:` scope. Secrets are never shown again after a key is made.",
        "responses": {
          "200": {
            "description": "The keys",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KeyList"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      },
      "post": {
        "tags": [
          "Keys"
        ],
        "operationId": "createKey",
        "summary": "Make a key",
        "description": "Needs the `cloud:keys:write` scope. A new key can hold only scopes and projects the calling key holds, so a\nkey can never make a stronger one. The secret is in this answer only.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/KeyCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The new key, with its secret shown this once",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KeyCreated"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound",
            "description": "A project in `projects` is not one this key reaches"
          },
          "409": {
            "$ref": "#/components/responses/Conflict",
            "description": "The first call with this `Idempotency-Key` is still running"
          },
          "422": {
            "$ref": "#/components/responses/BadRequest",
            "description": "This `Idempotency-Key` was used with a different request"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/v1/keys/{key_id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/KeyId"
        }
      ],
      "delete": {
        "tags": [
          "Keys"
        ],
        "operationId": "revokeKey",
        "summary": "Revoke a key",
        "description": "The key stops working within seconds, everywhere. A key can revoke itself.",
        "responses": {
          "204": {
            "description": "Revoked"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/ssh/certificates": {
      "post": {
        "tags": [
          "Files and commands"
        ],
        "operationId": "signSSHCertificate",
        "summary": "Sign a short-lived SSH certificate for a machine",
        "description": "Needs the `cloud:machines:exec` scope. Give a public key and a machine; get back an SSH certificate good for\nat most an hour, for that machine alone (its one principal is the machine's id, which is also the SSH user to\nconnect as). Present it to the SSH server (`ssh_host`) to open a real SSH session - for `scp`, `sftp` and\neditors' remote-SSH. There is no password or bare public-key login. `wm ssh-config` prints the matching\n`~/.ssh/config` block. A certificate works only while the key or the sign-in that asked for it does: revoking\nthat key, or signing out, ends the SSH sessions it opened, and it opens no more.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SSHCertificateRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The signed certificate",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SSHCertificate"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict",
            "description": "The first call with this `Idempotency-Key` is still running"
          },
          "422": {
            "$ref": "#/components/responses/BadRequest",
            "description": "This `Idempotency-Key` was used with a different request"
          },
          "501": {
            "$ref": "#/components/responses/NotImplemented"
          }
        }
      }
    },
    "/v1/ssh": {
      "get": {
        "tags": [
          "Files and commands"
        ],
        "operationId": "getSSH",
        "summary": "Whether real SSH is open here, and how to reach and trust it",
        "description": "Open when SSH certificates are signed here and the SSH server's address is set: what `wm ssh-config` needs to\npoint your own `ssh`, `scp` and `sftp` at a machine. Public keys only.\n",
        "responses": {
          "200": {
            "description": "The SSH door",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SSHDoor"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/v1/snapshots": {
      "get": {
        "tags": [
          "Machines"
        ],
        "operationId": "listSnapshots",
        "summary": "The account's snapshots, newest first",
        "description": "Needs the `cloud:machines:read` scope. Every snapshot taken through the API of a machine that is not deleted,\nin the projects this key reaches - each one a new machine can start from with `from_snapshot`. A deleted\nmachine's snapshots go with it.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/SnapshotLimit"
          }
        ],
        "responses": {
          "200": {
            "description": "The snapshots",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SnapshotList"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest",
            "description": "`limit` is not between 1 and 200"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/v1/machines": {
      "get": {
        "tags": [
          "Machines"
        ],
        "operationId": "listMachines",
        "summary": "Machines, newest first",
        "parameters": [
          {
            "name": "project",
            "in": "query",
            "description": "Only this project's machines.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "state",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/MachineState"
            }
          },
          {
            "name": "label",
            "in": "query",
            "description": "`key=value`; repeat to ask for every one of several labels.",
            "schema": {
              "type": "array",
              "items": {
                "type": "string"
              }
            },
            "style": "form",
            "explode": true
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/After"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of machines",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MachineList"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest",
            "description": "A `state`, `label`, `limit` or `after` that is not right"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound",
            "description": "The `project` is not one this key reaches"
          }
        }
      },
      "post": {
        "tags": [
          "Machines"
        ],
        "operationId": "createMachine",
        "summary": "Make a machine",
        "description": "Answers when the machine is running (201), usually in well under a second. When no server has room just now\none is started for it; if that takes longer than `wait_s`, the answer is `202` with the machine `starting`,\nand `GET /v1/machines/{machine_id}/wait` follows it.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MachineCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Running",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Machine"
                }
              }
            }
          },
          "202": {
            "description": "Starting; follow it with the wait call",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Machine"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound",
            "description": "The `project`, or the machine in `from_snapshot`, is not one this key reaches"
          },
          "409": {
            "$ref": "#/components/responses/Conflict",
            "description": "The first call with this `Idempotency-Key` is still running; or its internet is held back and no server here can hold back a machine's internet yet (`not_open`)"
          },
          "422": {
            "$ref": "#/components/responses/BadRequest",
            "description": "This `Idempotency-Key` was used with a different request"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/v1/machines/{machine_id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/MachineId"
        }
      ],
      "get": {
        "tags": [
          "Machines"
        ],
        "operationId": "getMachine",
        "summary": "One machine",
        "responses": {
          "200": {
            "description": "The machine",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Machine"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "patch": {
        "tags": [
          "Machines"
        ],
        "operationId": "updateMachine",
        "summary": "Rename a machine, move it to another project, or change its labels, secrets or internet",
        "description": "Labels merge: a label given `null` is taken off. A machine moves only to a project this key reaches, and\nkeeps everything of its own as it goes. Everything asked is changed at once, or nothing is. What it was made\nas - its size and when it sleeps by itself - stays as it was made: a new machine started from a snapshot of it\ncan be made otherwise.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MachineUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The machine as it is now",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Machine"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound",
            "description": "The machine, or the `project` it is moved to, is not one this key reaches"
          },
          "409": {
            "$ref": "#/components/responses/Conflict",
            "description": "Its internet was held back while it runs on a server that cannot hold it back (`not_open`): the rule is kept, and takes effect when it next starts on a server that can"
          }
        }
      },
      "delete": {
        "tags": [
          "Machines"
        ],
        "operationId": "deleteMachine",
        "summary": "Delete a machine and its files",
        "responses": {
          "204": {
            "description": "Deleted"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/machines/{machine_id}/start": {
      "parameters": [
        {
          "$ref": "#/components/parameters/MachineId"
        }
      ],
      "post": {
        "tags": [
          "Machines"
        ],
        "operationId": "startMachine",
        "summary": "Run it again, from paused, stopped or archived",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WaitOptions"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Running",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Machine"
                }
              }
            }
          },
          "202": {
            "description": "Starting",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Machine"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest",
            "description": "`wait_s` is not between 0 and 600"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict",
            "description": "The machine cannot start in the state it is in; or its internet is held back and no server here can hold back a machine's internet yet (`not_open`)"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests",
            "description": "Too many calls just now, or the account has as many machines running or kept as it may"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/v1/machines/{machine_id}/pause": {
      "parameters": [
        {
          "$ref": "#/components/parameters/MachineId"
        }
      ],
      "post": {
        "tags": [
          "Machines"
        ],
        "operationId": "pauseMachine",
        "summary": "Pause it with its memory kept",
        "description": "Nothing runs and no running time is charged; `start` brings it back where it was.",
        "responses": {
          "200": {
            "description": "Paused",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Machine"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        }
      }
    },
    "/v1/machines/{machine_id}/stop": {
      "parameters": [
        {
          "$ref": "#/components/parameters/MachineId"
        }
      ],
      "post": {
        "tags": [
          "Machines"
        ],
        "operationId": "stopMachine",
        "summary": "Stop it; its files stay",
        "responses": {
          "200": {
            "description": "Stopped",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Machine"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict",
            "description": "The machine cannot stop in the state it is in"
          }
        }
      }
    },
    "/v1/machines/{machine_id}/archive": {
      "parameters": [
        {
          "$ref": "#/components/parameters/MachineId"
        }
      ],
      "post": {
        "tags": [
          "Machines"
        ],
        "operationId": "archiveMachine",
        "summary": "Put its files away in storage, the cheapest way to keep it",
        "description": "Sealed with the account's key when the machine's files are sealed. `start` brings it back.",
        "responses": {
          "200": {
            "description": "Archived",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Machine"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict",
            "description": "The machine cannot be archived in the state it is in"
          }
        }
      }
    },
    "/v1/machines/{machine_id}/copy": {
      "parameters": [
        {
          "$ref": "#/components/parameters/MachineId"
        }
      ],
      "post": {
        "tags": [
          "Machines"
        ],
        "operationId": "copyMachine",
        "summary": "A new machine, a copy of this one as it is now",
        "description": "Made beside the original where there is room, so the files are copied in a moment. The copy's internet is held\nback as the original's is: when it is and no server here can hold back a machine's internet yet, the answer is\n`409 not_open`.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MachineCopy"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The copy, running",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Machine"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest",
            "description": "A name, label or `wait_s` that is not right"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict",
            "description": "The first call with this `Idempotency-Key` is still running"
          },
          "422": {
            "$ref": "#/components/responses/BadRequest",
            "description": "This `Idempotency-Key` was used with a different request"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/v1/machines/{machine_id}/snapshots": {
      "parameters": [
        {
          "$ref": "#/components/parameters/MachineId"
        }
      ],
      "post": {
        "tags": [
          "Machines"
        ],
        "operationId": "snapshotMachine",
        "summary": "Keep its files as they are now",
        "description": "A new machine can be made from a snapshot with `from_snapshot`.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "properties": {
                  "note": {
                    "type": "string",
                    "maxLength": 200
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The snapshot",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Snapshot"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest",
            "description": "The note is longer than 200 characters"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/BadRequest",
            "description": "This `Idempotency-Key` was used with a different request"
          }
        }
      },
      "get": {
        "tags": [
          "Machines"
        ],
        "operationId": "listMachineSnapshots",
        "summary": "The machine's snapshots, newest first",
        "description": "Needs the `cloud:machines:read` scope. Those taken through the API; a deleted machine has none.",
        "parameters": [
          {
            "$ref": "#/components/parameters/SnapshotLimit"
          }
        ],
        "responses": {
          "200": {
            "description": "Its snapshots",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SnapshotList"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest",
            "description": "`limit` is not between 1 and 200"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/machines/{machine_id}/wait": {
      "parameters": [
        {
          "$ref": "#/components/parameters/MachineId"
        }
      ],
      "get": {
        "tags": [
          "Machines"
        ],
        "operationId": "waitForMachine",
        "summary": "Answer when the machine reaches a state",
        "parameters": [
          {
            "name": "state",
            "in": "query",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/MachineState"
            }
          },
          {
            "name": "timeout_s",
            "in": "query",
            "schema": {
              "type": "number",
              "minimum": 0,
              "maximum": 600,
              "default": 60
            }
          }
        ],
        "responses": {
          "200": {
            "description": "It is in that state",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Machine"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest",
            "description": "No `state`, or a `timeout_s` that is not between 0 and 600"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "408": {
            "$ref": "#/components/responses/Timeout"
          }
        }
      }
    },
    "/v1/machines/{machine_id}/metrics": {
      "parameters": [
        {
          "$ref": "#/components/parameters/MachineId"
        }
      ],
      "get": {
        "tags": [
          "Machines"
        ],
        "operationId": "getMachineMetrics",
        "summary": "What the machine used, step by step, over the last day",
        "description": "Needs the `cloud:machines:read` scope. Its processors as a share of its own size, the most memory it held and\nthe most it may hold, and the bytes a second it read and wrote on its disk and received and sent on its\nnetwork - a point for each `step` from `since` on. Its server reads it every few seconds while it runs and\nkeeps a point a minute for the last day, in memory: a step it was paused or stopped through has no point, and\nthe numbers begin again when the machine moves to another server or its server's program starts anew\n(`kept_since` says how far back they go). A machine that is not on a server just now has no points.\n",
        "parameters": [
          {
            "name": "since",
            "in": "query",
            "description": "From when: a time (RFC 3339), or how long ago (`90s`, `15m`, `6h`, `1d`); the last hour when left out, and at most a day back.",
            "schema": {
              "type": "string",
              "examples": [
                "6h",
                "2026-10-23T09:00:00Z"
              ]
            }
          },
          {
            "name": "step",
            "in": "query",
            "description": "How long each point covers: whole minutes from 1 to 60, in seconds (`300`) or as a span (`5m`).",
            "schema": {
              "type": "string",
              "default": "60",
              "examples": [
                "60",
                "5m",
                "1h"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Its points, the oldest first",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MachineMetrics"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "501": {
            "$ref": "#/components/responses/NotImplemented",
            "description": "It runs on a server that does not keep its numbers yet: none come while it runs there"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/v1/machines/{machine_id}/exec": {
      "parameters": [
        {
          "$ref": "#/components/parameters/MachineId"
        }
      ],
      "post": {
        "tags": [
          "Files and commands"
        ],
        "operationId": "execInMachine",
        "summary": "Run a command in the machine and get what it wrote",
        "description": "Needs the `cloud:machines:exec` scope. A paused machine is woken for it. Commands run as the machine's user\nunless `root` is asked for; root is inside the machine only, never on our servers.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ExecRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "It ran; its exit code says how it ended",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExecResult"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict",
            "description": "The machine cannot run a command in the state it is in; or its internet is held back and no server here can hold back a machine's internet yet (`not_open`)"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable",
            "description": "The command's result did not come back: it may or may not have run, so check before running it again"
          },
          "504": {
            "$ref": "#/components/responses/Timeout",
            "description": "The command ran longer than 10 minutes; it may still be running in the machine"
          }
        }
      }
    },
    "/v1/machines/{machine_id}/terminal": {
      "parameters": [
        {
          "$ref": "#/components/parameters/MachineId"
        }
      ],
      "get": {
        "tags": [
          "Files and commands"
        ],
        "operationId": "openTerminal",
        "summary": "Open an interactive terminal in the machine (WebSocket)",
        "description": "Needs the `cloud:machines:exec` scope. This route is a WebSocket upgrade, not a plain request: connect with\n`Upgrade: websocket` (version 13) and the usual `Authorization: Bearer` header. A paused machine is woken for\nit; the terminal is a login shell unless a command is given, as the machine's user unless `root` is asked for.\n\nOver the socket, each message is one event. From you: a **binary** message is typed in (keystrokes, bytes as\nthey are); a **text** message `{\"resize\":{\"cols\":C,\"rows\":R}}` sets the window. From us: a **binary** message\nis what the program wrote on its terminal; a final **text** message `{\"exit_code\":N}` (or `{\"reason\":...,\n\"message\":...}` when it did not end by itself) says how it ended, just before the socket closes.\n\nA caller may ask for the protocol `wm.terminal` on the socket, and is answered with it; the dashboard's page\ndoes, beside its sign-in's token (`wm.csrf.<token>`), from its own address. Such a caller - a browser is never\nshown the answer to a handshake it was refused - is told a terminal that cannot open over the socket instead:\nit is switched, sent one text message `{\"reason\": <the code>, \"status\": <the status>, \"message\": ...}`, and\nclosed with the code 4000 and that status (4501: not open for this machine). Any other caller is answered as\nbelow, before anything is switched.\n\nAn open terminal stays open only while its caller still may hold it. When the sign-in it was opened with ends\n(signed out, here or from another browser), the key is revoked or expires, or the account is held or can no\nlonger spend, it is ended - at once, or at the latest at the next half-minute ping - and every caller is told\nwhy the same way: one text message `{\"reason\": ..., \"status\": ..., \"message\": ...}` (4401 signed out or the key\nrevoked, 4403 the account held, 4402 it cannot spend), then a close with the code 4000 and that status.\n\nWe never log what is typed or shown. The audit records only that a terminal opened and closed.\n",
        "parameters": [
          {
            "name": "cols",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 4096,
              "default": 80
            }
          },
          {
            "name": "rows",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 4096,
              "default": 24
            }
          },
          {
            "name": "user",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "user",
                "root"
              ],
              "default": "user"
            }
          },
          {
            "name": "term",
            "in": "query",
            "description": "The terminal's kind, set as `TERM` inside (default `xterm-256color`).",
            "schema": {
              "type": "string",
              "maxLength": 64
            }
          },
          {
            "name": "cwd",
            "in": "query",
            "description": "Where it starts, an absolute path; the work folder when left out.",
            "schema": {
              "type": "string",
              "maxLength": 1024
            }
          },
          {
            "name": "cmd",
            "in": "query",
            "description": "A word of the command to run; repeat for each word. Left out, a login shell opens.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "101": {
            "description": "Switching to the terminal's WebSocket"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "426": {
            "$ref": "#/components/responses/BadRequest"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "501": {
            "$ref": "#/components/responses/NotImplemented"
          }
        }
      }
    },
    "/v1/machines/{machine_id}/files": {
      "parameters": [
        {
          "$ref": "#/components/parameters/MachineId"
        },
        {
          "name": "path",
          "in": "query",
          "required": true,
          "description": "Inside the machine's home (`home/...`) or its work folder, where commands start (`work/...`).",
          "schema": {
            "type": "string",
            "maxLength": 1024
          }
        }
      ],
      "get": {
        "tags": [
          "Files and commands"
        ],
        "operationId": "readFile",
        "summary": "Read a file",
        "description": "Needs the `cloud:machines:exec` scope.",
        "responses": {
          "200": {
            "description": "The file's bytes",
            "content": {
              "application/octet-stream": {
                "schema": {
                  "type": "string",
                  "contentMediaType": "application/octet-stream"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        }
      },
      "put": {
        "tags": [
          "Files and commands"
        ],
        "operationId": "writeFile",
        "summary": "Write a file, making the folders on the way",
        "description": "Needs the `cloud:machines:exec` scope and a `Content-Length`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/octet-stream": {
              "schema": {
                "type": "string",
                "contentMediaType": "application/octet-stream"
              }
            }
          }
        },
        "responses": {
          "204": {
            "description": "Written"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "411": {
            "$ref": "#/components/responses/BadRequest"
          },
          "413": {
            "$ref": "#/components/responses/TooLarge"
          }
        }
      },
      "delete": {
        "tags": [
          "Files and commands"
        ],
        "operationId": "deleteFile",
        "summary": "Delete a file, or a folder",
        "description": "Needs the `cloud:machines:exec` scope. A folder goes only when it is empty, unless `recursive` is true: then\neverything in it goes with it. A link is taken away as itself, never what it points to. The home and the work\nfolder themselves cannot be deleted.\n",
        "parameters": [
          {
            "name": "recursive",
            "in": "query",
            "description": "With a folder, everything in it too.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict",
            "description": "The folder is not empty and `recursive` is not true, it belongs to another user of the machine, or the machine cannot reach its files in the state it is in"
          }
        }
      }
    },
    "/v1/machines/{machine_id}/folder": {
      "parameters": [
        {
          "$ref": "#/components/parameters/MachineId"
        },
        {
          "name": "path",
          "in": "query",
          "required": true,
          "description": "A folder of the machine's home (`home`, `home/...`) or of its work folder, where commands start (`work`, `work/...`).",
          "schema": {
            "type": "string",
            "maxLength": 1024
          }
        }
      ],
      "get": {
        "tags": [
          "Files and commands"
        ],
        "operationId": "listFolder",
        "summary": "What a folder holds",
        "description": "Needs the `cloud:machines:exec` scope. Its own entries, folders first and then by name, each with its kind, its\nsize and when it last changed; at most 1,000, and `truncated` says when there were more. A paused machine is\nwoken for it.\n",
        "responses": {
          "200": {
            "description": "The folder",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Folder"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest",
            "description": "The path is not one of the machine's home or work folder, or it names a file"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict",
            "description": "The machine cannot reach its files in the state it is in"
          },
          "501": {
            "$ref": "#/components/responses/NotImplemented",
            "description": "The machine has no `find` that can say what each file is"
          }
        }
      }
    },
    "/v1/machines/{machine_id}/ports": {
      "parameters": [
        {
          "$ref": "#/components/parameters/MachineId"
        }
      ],
      "get": {
        "tags": [
          "Ports"
        ],
        "operationId": "listPorts",
        "summary": "The machine's ports open at web addresses",
        "description": "Needs the `cloud:machines:read` scope.",
        "responses": {
          "200": {
            "description": "Its open ports, lowest first",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PortList"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "post": {
        "tags": [
          "Ports"
        ],
        "operationId": "openPort",
        "summary": "Open a port of the machine at a web address of its own",
        "description": "Needs the `cloud:machines:write` scope. Private unless `public` is true. A port that is open already is\nanswered `200`, made public or private as asked (left out, `public` is false). The ports the machine itself\nuses (5900, 6080, 7070, 9222, 9223 and 9224) cannot be opened (`400`), and a machine has at most 20 open\n(`429` with the code `limit_reached`). Until web addresses are served, the answer is `503` with the code\n`not_open`, which waiting a moment does not change.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PortOpen"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "It was open already; it is public or private as asked",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Port"
                }
              }
            }
          },
          "201": {
            "description": "Open",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Port"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/v1/machines/{machine_id}/ports/{port}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/MachineId"
        },
        {
          "$ref": "#/components/parameters/Port"
        }
      ],
      "delete": {
        "tags": [
          "Ports"
        ],
        "operationId": "closePort",
        "summary": "Close a port's web address",
        "description": "Needs the `cloud:machines:write` scope. The address answers 404 from then on, and links made for it no longer\nopen it. Closing a port that is not open is answered `204` all the same.\n",
        "responses": {
          "204": {
            "description": "Closed"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/machines/{machine_id}/ports/{port}/link": {
      "parameters": [
        {
          "$ref": "#/components/parameters/MachineId"
        },
        {
          "$ref": "#/components/parameters/Port"
        }
      ],
      "post": {
        "tags": [
          "Ports"
        ],
        "operationId": "linkPort",
        "summary": "A link that lets one browser into a private address for a while",
        "description": "Needs the `cloud:machines:read` scope. The link opens once, within five minutes. The browser that opens it\nis given access to that one address for `access_s` seconds (a cookie for that address alone) and is taken to\n`path` on it; a page that only previews links does not use it up. The link is shown in this answer only, and\neach call makes a new one: it is not sent again once it may have reached the API. A port that is not open is\nanswered `404`; until web addresses are served, `503` with the code `not_open`.\n",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PortLinkCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The link",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PortLink"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/v1/secrets": {
      "get": {
        "tags": [
          "Secrets"
        ],
        "operationId": "listSecrets",
        "summary": "The account's secrets, by name, and the machines that get each",
        "description": "Needs the `cloud:machines:read` scope. A secret's value is never shown again once it is set: not here, not in\nany other answer, not in the audit. A key limited to some projects lists only the secrets its projects'\nmachines get, and sees only those machines.\n",
        "responses": {
          "200": {
            "description": "The secrets",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SecretList"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "503": {
            "$ref": "#/components/responses/NotSetUp"
          }
        }
      }
    },
    "/v1/secrets/{name}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/SecretName"
        }
      ],
      "put": {
        "tags": [
          "Secrets"
        ],
        "operationId": "setSecret",
        "summary": "Set a secret, or set it anew",
        "description": "Needs the `cloud:secrets:write` scope and a key of the whole account: secrets belong to the account, and a key\nlimited to some projects cannot set, delete or give them (403). The value is sealed at once with a key of the\naccount's own and kept only so; the machines given the secret get the new value from their next command. An\naccount keeps at most 200 secrets.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SecretSet"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Set anew",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Secret"
                }
              }
            }
          },
          "201": {
            "description": "Set for the first time",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Secret"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "413": {
            "$ref": "#/components/responses/TooLarge"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/NotSetUp"
          }
        }
      },
      "delete": {
        "tags": [
          "Secrets"
        ],
        "operationId": "deleteSecret",
        "summary": "Delete a secret",
        "description": "Needs the `cloud:secrets:write` scope and a key of the whole account. The secret is taken off every machine\nthat was given it: one set again later under the same name goes to none of them until it is given anew.\n",
        "responses": {
          "204": {
            "description": "Deleted"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "503": {
            "$ref": "#/components/responses/NotSetUp"
          }
        }
      }
    },
    "/v1/sizes": {
      "get": {
        "tags": [
          "Catalogue"
        ],
        "operationId": "listSizes",
        "summary": "The sizes a machine without a GPU comes in, and what an hour of each costs",
        "description": "What an hour of a machine of each size running comes to - the price its running time is counted at. A paused machine runs no time.",
        "responses": {
          "200": {
            "description": "The sizes, the smallest first",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SizeList"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/v1/regions": {
      "get": {
        "tags": [
          "Catalogue"
        ],
        "operationId": "listRegions",
        "summary": "Places a machine can be asked to run near",
        "description": "The machine says where it actually runs in `location`.",
        "responses": {
          "200": {
            "description": "The places",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "object",
                    "data"
                  ],
                  "properties": {
                    "object": {
                      "const": "list"
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Region"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/v1/gpus": {
      "get": {
        "tags": [
          "Catalogue"
        ],
        "operationId": "listGpus",
        "summary": "GPUs, where they are and what an hour costs",
        "responses": {
          "200": {
            "description": "The GPUs on offer now",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GpuList"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/v1/usage": {
      "get": {
        "tags": [
          "Account"
        ],
        "operationId": "getUsage",
        "summary": "What each machine ran for and cost in a month",
        "parameters": [
          {
            "name": "month",
            "in": "query",
            "description": "`YYYY-MM`; this month when left out.",
            "schema": {
              "type": "string",
              "pattern": "^[0-9]{4}-[0-9]{2}$"
            }
          },
          {
            "name": "project",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The month's usage",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Usage"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest",
            "description": "`month` is not `YYYY-MM`"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound",
            "description": "The `project` is not one this key reaches"
          }
        }
      }
    },
    "/v1/login/devices": {
      "post": {
        "tags": [
          "Sign-in"
        ],
        "operationId": "startDeviceLogin",
        "summary": "Start signing a program in through the browser",
        "description": "For a program on a computer with a browser (`wm login`): no key is needed. The answer holds `device_code`,\nwhich the program keeps to itself, and `user_code`, which the person types at `verification_uri`, signed in\nto the dashboard, and approves. The code is never carried in an address (there is no\n`verification_uri_complete`): a link with someone else's code in it is how such sign-ins are stolen. The\ncodes last 10 minutes and are used once. Then ask for the key every `interval` seconds\n(`/v1/login/devices/token`).\n",
        "security": [],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DeviceLoginRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The sign-in, waiting for the person",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeviceLoginStarted"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/v1/login/devices/token": {
      "post": {
        "tags": [
          "Sign-in"
        ],
        "operationId": "getDeviceLoginKey",
        "summary": "The program's key, once the person approved",
        "description": "`202` while the person has not decided; `201` with the new key, its secret in this answer only, once they\napproved - named as the program asked (\"wm on laptop\") and holding the scopes they allowed; `403\nlogin_denied` when they refused (or signed out since); `410 login_expired` when the code ran out or was\nused. Asked more often than `interval`: `429` with `Retry-After`.\n",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DeviceLoginToken"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The new key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KeyCreated"
                }
              }
            }
          },
          "202": {
            "description": "Still waiting for the person",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeviceLoginPending"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "410": {
            "$ref": "#/components/responses/Gone"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/v1/login/devices/lookup": {
      "post": {
        "tags": [
          "Sign-in"
        ],
        "operationId": "lookupDeviceLogin",
        "summary": "What a program asks, by the code the person typed",
        "description": "The dashboard's own, signed in. The code goes in the body, never in an address.",
        "security": [
          {
            "session": [],
            "csrf": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DeviceLoginDecision"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The program's sign-in, waiting",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeviceLogin"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/v1/login/devices/approve": {
      "post": {
        "tags": [
          "Sign-in"
        ],
        "operationId": "approveDeviceLogin",
        "summary": "Let a program in",
        "description": "The dashboard's own, signed in. `scopes` narrows what the program asked for; it never widens it.",
        "security": [
          {
            "session": [],
            "csrf": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DeviceLoginDecision"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Approved; the program is given its key when it next asks",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeviceLogin"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/v1/login/devices/deny": {
      "post": {
        "tags": [
          "Sign-in"
        ],
        "operationId": "denyDeviceLogin",
        "summary": "Refuse a program",
        "security": [
          {
            "session": [],
            "csrf": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DeviceLoginDecision"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Refused; the program is told so",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeviceLogin"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        }
      }
    },
    "/v1/session": {
      "delete": {
        "tags": [
          "Sign-in"
        ],
        "operationId": "signOut",
        "summary": "Sign out of the dashboard here",
        "security": [
          {
            "session": [],
            "csrf": []
          }
        ],
        "responses": {
          "204": {
            "description": "Signed out"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/v1/sessions": {
      "get": {
        "tags": [
          "Sign-in"
        ],
        "operationId": "listSessions",
        "summary": "Where the account is signed in to the dashboard",
        "security": [
          {
            "session": [],
            "csrf": []
          }
        ],
        "responses": {
          "200": {
            "description": "Every sign-in that still works, this one marked `current`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SessionList"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      },
      "delete": {
        "tags": [
          "Sign-in"
        ],
        "operationId": "signOutEverywhere",
        "summary": "Sign out of the dashboard everywhere",
        "description": "Every sign-in of the account ends, this one too. Keys are not touched; revoke those on their own.",
        "security": [
          {
            "session": [],
            "csrf": []
          }
        ],
        "responses": {
          "204": {
            "description": "Signed out everywhere"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/v1/sessions/{session_id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/SessionId"
        }
      ],
      "delete": {
        "tags": [
          "Sign-in"
        ],
        "operationId": "signOutBrowser",
        "summary": "Sign one browser out of the dashboard",
        "description": "That sign-in ends, and no other; this one too when it is this one. Another account's sign-in, or one that has ended, is not found.",
        "security": [
          {
            "session": [],
            "csrf": []
          }
        ],
        "responses": {
          "204": {
            "description": "Signed out"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/auth/providers": {
      "servers": [
        {
          "url": "https://app.wheelmachine.ai"
        }
      ],
      "get": {
        "tags": [
          "Sign-in"
        ],
        "operationId": "signInProviders",
        "summary": "Which ways of signing in are open",
        "description": "A way that is not set up yet says `false`, and its door sends the browser back with `signin=not_set_up`.",
        "security": [],
        "responses": {
          "200": {
            "description": "The doors",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Providers"
                }
              }
            }
          }
        }
      }
    },
    "/auth/github/start": {
      "servers": [
        {
          "url": "https://app.wheelmachine.ai"
        }
      ],
      "get": {
        "tags": [
          "Sign-in"
        ],
        "operationId": "signInWithGitHub",
        "summary": "Continue with GitHub",
        "description": "Sends the browser to GitHub with a state, PKCE and the scopes `read:user user:email`. `then` is a place on the\ndashboard to come back to (`/device`, `/keys`); anything else is dropped.\n",
        "security": [],
        "parameters": [
          {
            "$ref": "#/components/parameters/Then"
          }
        ],
        "responses": {
          "302": {
            "description": "On to GitHub, or back to the dashboard with `signin=` saying why not"
          }
        }
      }
    },
    "/auth/github/callback": {
      "servers": [
        {
          "url": "https://app.wheelmachine.ai"
        }
      ],
      "get": {
        "tags": [
          "Sign-in"
        ],
        "operationId": "signInWithGitHubBack",
        "summary": "Back from GitHub",
        "description": "Signs the person in when the state is the one this browser set out with, in time, and GitHub verified an\ne-mail address of theirs; the account is found - by the GitHub account's number, else by that address - or\nmade. Otherwise back to the dashboard with `signin=` one of `state`, `cancelled`, `unverified`, `held`,\n`young`, `unavailable`, `failed`.\n",
        "security": [],
        "responses": {
          "302": {
            "description": "Signed in and on to the dashboard, or back to it with `signin=` saying why not"
          }
        }
      }
    },
    "/auth/google/start": {
      "servers": [
        {
          "url": "https://app.wheelmachine.ai"
        }
      ],
      "get": {
        "tags": [
          "Sign-in"
        ],
        "operationId": "signInWithGoogle",
        "summary": "Continue with Google",
        "description": "Sends the browser to Google with a state, PKCE, a nonce and the scopes `openid email profile`.",
        "security": [],
        "parameters": [
          {
            "$ref": "#/components/parameters/Then"
          }
        ],
        "responses": {
          "302": {
            "description": "On to Google, or back to the dashboard with `signin=` saying why not"
          }
        }
      }
    },
    "/auth/google/callback": {
      "servers": [
        {
          "url": "https://app.wheelmachine.ai"
        }
      ],
      "get": {
        "tags": [
          "Sign-in"
        ],
        "operationId": "signInWithGoogleBack",
        "summary": "Back from Google",
        "description": "As GitHub's way back; the ID token must be Google's, for this application, in time, this sign-in's (nonce), with `email_verified`.",
        "security": [],
        "responses": {
          "302": {
            "description": "Signed in and on to the dashboard, or back to it with `signin=` saying why not"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "key": {
        "type": "http",
        "scheme": "bearer",
        "description": "An API key: `ak_live_` and 40 characters."
      },
      "session": {
        "type": "apiKey",
        "in": "cookie",
        "name": "__Host-wm_session",
        "description": "The person's sign-in to the dashboard, on https://app.wheelmachine.ai only. It lasts 30 days from its last call."
      },
      "csrf": {
        "type": "apiKey",
        "in": "header",
        "name": "X-CSRF-Token",
        "description": "The sign-in's token (the cookie `__Host-wm_csrf`, which the dashboard's page reads), on every call made with the\nsign-in; a call that changes anything must also come from the dashboard's address (`Origin`).\n"
      }
    },
    "parameters": {
      "MachineId": {
        "name": "machine_id",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "pattern": "^m_[0-9a-z]{26}$"
        }
      },
      "ProjectId": {
        "name": "project_id",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "pattern": "^prj_[0-9a-z]{26}$"
        }
      },
      "SessionId": {
        "name": "session_id",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "pattern": "^ses_[0-9a-z]{26}$"
        }
      },
      "SnapshotLimit": {
        "name": "limit",
        "in": "query",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "maximum": 200,
          "default": 100
        }
      },
      "KeyId": {
        "name": "key_id",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "pattern": "^key_[0-9a-f]{16}$"
        }
      },
      "Port": {
        "name": "port",
        "in": "path",
        "required": true,
        "schema": {
          "type": "integer",
          "minimum": 1,
          "maximum": 65535
        }
      },
      "SecretName": {
        "name": "name",
        "in": "path",
        "required": true,
        "description": "Written as an environment variable's name is, which is what a machine's commands get it as.",
        "schema": {
          "type": "string",
          "pattern": "^[A-Z_][A-Z0-9_]{0,63}$",
          "examples": [
            "DATABASE_URL"
          ]
        }
      },
      "Limit": {
        "name": "limit",
        "in": "query",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "maximum": 200,
          "default": 50
        }
      },
      "After": {
        "name": "after",
        "in": "query",
        "description": "The `next` of the page before.",
        "schema": {
          "type": "string"
        }
      },
      "Then": {
        "name": "then",
        "in": "query",
        "description": "Where on the dashboard to go once signed in, such as `/device`.",
        "schema": {
          "type": "string"
        }
      },
      "IdempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "description": "Any string up to 255 characters, unique per attempt. The same key with the same request within 24 hours gets\nthe first answer again (with `Idempotent-Replayed: true`); with a different request it is refused.\n",
        "schema": {
          "type": "string",
          "maxLength": 255
        }
      }
    },
    "headers": {
      "RequestId": {
        "description": "This call's id.",
        "schema": {
          "type": "string"
        }
      },
      "RetryAfter": {
        "description": "Seconds to wait before trying again.",
        "schema": {
          "type": "integer"
        }
      }
    },
    "responses": {
      "BadRequest": {
        "description": "The request is not right",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      },
      "Unauthorized": {
        "description": "No key, or a key that does not work",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      },
      "PaymentRequired": {
        "description": "The account cannot spend now - its limit for this month is reached, or the account is held",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      },
      "Forbidden": {
        "description": "The key does not hold the scope or project this needs",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      },
      "NotFound": {
        "description": "Nothing of that id that this key can see",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      },
      "Timeout": {
        "description": "The state was not reached in time",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      },
      "Conflict": {
        "description": "Not possible in the state it is in",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      },
      "NotImplemented": {
        "description": "Not open for this machine",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      },
      "Gone": {
        "description": "The sign-in's code ran out, or was used already",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      },
      "TooLarge": {
        "description": "Too large",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      },
      "TooManyRequests": {
        "description": "A limit is reached; `Retry-After` says when to try again",
        "headers": {
          "Retry-After": {
            "$ref": "#/components/headers/RetryAfter"
          }
        },
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      },
      "Unavailable": {
        "description": "No room just now; `Retry-After` says when to try again",
        "headers": {
          "Retry-After": {
            "$ref": "#/components/headers/RetryAfter"
          }
        },
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      },
      "NotSetUp": {
        "description": "Secrets are not set up on this API yet (`unavailable`, with a `Retry-After` of an hour)",
        "headers": {
          "Retry-After": {
            "$ref": "#/components/headers/RetryAfter"
          }
        },
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      }
    },
    "schemas": {
      "Problem": {
        "type": "object",
        "required": [
          "type",
          "title",
          "status",
          "code"
        ],
        "properties": {
          "type": {
            "type": "string",
            "description": "A link to the page that explains this `code`."
          },
          "title": {
            "type": "string"
          },
          "status": {
            "type": "integer"
          },
          "detail": {
            "type": "string"
          },
          "code": {
            "type": "string",
            "enum": [
              "invalid_request",
              "unauthorized",
              "key_revoked",
              "key_expired",
              "forbidden",
              "not_found",
              "conflict",
              "project_not_empty",
              "payment_required",
              "rate_limited",
              "limit_reached",
              "unavailable",
              "timeout",
              "too_large",
              "not_open",
              "internal",
              "login_denied",
              "login_expired"
            ]
          },
          "request_id": {
            "type": "string"
          },
          "retry_after_s": {
            "type": "number"
          }
        }
      },
      "Me": {
        "type": "object",
        "required": [
          "object",
          "account",
          "key",
          "standing"
        ],
        "properties": {
          "object": {
            "const": "me"
          },
          "account": {
            "type": "object",
            "required": [
              "id"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string",
                "description": "With the dashboard's sign-in: the person's name, as their door gave it."
              },
              "email": {
                "type": "string",
                "description": "With the dashboard's sign-in: the e-mail address GitHub or Google verified."
              }
            }
          },
          "key": {
            "description": "The key this call is made with; null for a call made with the dashboard's sign-in.",
            "oneOf": [
              {
                "$ref": "#/components/schemas/Key"
              },
              {
                "type": "null"
              }
            ]
          },
          "session": {
            "description": "The dashboard's sign-in this call is made with, when it is.",
            "$ref": "#/components/schemas/Session"
          },
          "standing": {
            "$ref": "#/components/schemas/Standing"
          }
        }
      },
      "Standing": {
        "type": "object",
        "required": [
          "can_spend"
        ],
        "properties": {
          "can_spend": {
            "type": "boolean",
            "description": "False when the account's limit for this month is reached or the account is held."
          },
          "balance_usd": {
            "type": [
              "number",
              "null"
            ],
            "description": "What is left to spend this month under the account's limit; null for an account with no money limit."
          },
          "reason": {
            "type": "string",
            "description": "Why it cannot spend, in words a person reads."
          }
        }
      },
      "Project": {
        "type": "object",
        "required": [
          "id",
          "object",
          "name",
          "created_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "examples": [
              "prj_01jb2xk3n4p5q6r7s8t9v0w1x2"
            ]
          },
          "object": {
            "const": "project"
          },
          "name": {
            "type": "string"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ProjectCreate": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "name"
        ],
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 80
          }
        }
      },
      "ProjectUpdate": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "name"
        ],
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 80
          }
        }
      },
      "ProjectList": {
        "type": "object",
        "required": [
          "object",
          "data",
          "next"
        ],
        "properties": {
          "object": {
            "const": "list"
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Project"
            }
          },
          "next": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "Scope": {
        "type": "string",
        "description": "`cloud:machines:read` see machines, and the names of secrets; `cloud:machines:write` make, change, pause,\ncopy and delete them; `cloud:machines:exec` run commands and read or write files in them; `cloud:gpus` make\nGPU machines; `cloud:projects:write` make and change projects; `cloud:keys:write` make and revoke keys;\n`cloud:secrets:write` set and delete secrets, and give them to machines; `cloud:*` all of these.\n",
        "enum": [
          "cloud:*",
          "cloud:machines:read",
          "cloud:machines:write",
          "cloud:machines:exec",
          "cloud:gpus",
          "cloud:projects:write",
          "cloud:keys:write",
          "cloud:secrets:write"
        ]
      },
      "Key": {
        "type": "object",
        "required": [
          "id",
          "object",
          "name",
          "scopes",
          "projects",
          "shown",
          "state",
          "created_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "examples": [
              "key_3f9a1c2b4d5e6f70"
            ]
          },
          "object": {
            "const": "key"
          },
          "name": {
            "type": "string"
          },
          "scopes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Scope"
            }
          },
          "projects": {
            "type": "array",
            "description": "The projects it is limited to; empty for every project of the account.",
            "items": {
              "type": "string"
            }
          },
          "shown": {
            "type": "string",
            "description": "Its first and last characters, to recognise it by.",
            "examples": [
              "ak_live_abcd…wxyz"
            ]
          },
          "state": {
            "type": "string",
            "enum": [
              "active",
              "revoked",
              "expired"
            ]
          },
          "rate_per_minute": {
            "type": [
              "integer",
              "null"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "expires_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "last_used_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "KeyCreate": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "name",
          "scopes"
        ],
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 80
          },
          "scopes": {
            "type": "array",
            "minItems": 1,
            "items": {
              "$ref": "#/components/schemas/Scope"
            }
          },
          "projects": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          },
          "rate_per_minute": {
            "type": "integer",
            "minimum": 1,
            "maximum": 6000
          }
        }
      },
      "KeyCreated": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Key"
          },
          {
            "type": "object",
            "required": [
              "secret"
            ],
            "properties": {
              "secret": {
                "type": "string",
                "description": "The key itself. Shown in this answer only."
              }
            }
          }
        ]
      },
      "KeyList": {
        "type": "object",
        "required": [
          "object",
          "data"
        ],
        "properties": {
          "object": {
            "const": "list"
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Key"
            }
          }
        }
      },
      "MachineState": {
        "type": "string",
        "description": "`starting` being made ready; `running`; `paused` memory kept, nothing running; `stopped` files kept, memory\nnot; `archived` files in storage; `recovering` its server was lost and it is being brought back from its last\nmoments; `deleted`.\n",
        "enum": [
          "starting",
          "running",
          "paused",
          "stopped",
          "archived",
          "recovering",
          "deleted"
        ]
      },
      "Machine": {
        "type": "object",
        "required": [
          "id",
          "object",
          "name",
          "project",
          "state",
          "labels",
          "network",
          "files_sealed",
          "created_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "examples": [
              "m_01jb2xk3n4p5q6r7s8t9v0w1x2"
            ]
          },
          "object": {
            "const": "machine"
          },
          "name": {
            "type": "string"
          },
          "project": {
            "type": "string"
          },
          "state": {
            "$ref": "#/components/schemas/MachineState"
          },
          "gpu": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/GpuRef"
              }
            ]
          },
          "size": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "cpus": {
                "type": "integer"
              },
              "memory_gb": {
                "type": "number"
              },
              "disk_gb": {
                "type": "number"
              }
            }
          },
          "location": {
            "type": [
              "object",
              "null"
            ],
            "description": "Where it runs now.",
            "properties": {
              "city": {
                "type": "string"
              },
              "country": {
                "type": "string",
                "description": "ISO 3166-1 alpha-2"
              }
            }
          },
          "cpus": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Its processors, with 2 GB of memory each; null for a GPU machine, whose size comes with its GPU.",
            "enum": [
              2,
              4,
              8,
              16,
              null
            ]
          },
          "memory_gb": {
            "type": [
              "number",
              "null"
            ],
            "description": "Its memory; null for a GPU machine."
          },
          "sleep_when_idle": {
            "type": "boolean",
            "description": "True: it pauses by itself once it is idle - no call or connection has reached it for `idle_after_s` and\nits programs have been quiet as long. False: it never pauses by itself.\n"
          },
          "idle_after_s": {
            "type": [
              "integer",
              "null"
            ],
            "description": "How long it must be idle before it pauses by itself; null when it never does."
          },
          "labels": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "secrets": {
            "type": "array",
            "description": "The names of the secrets every command run in it gets as environment variables; never their values.",
            "items": {
              "type": "string"
            }
          },
          "network": {
            "$ref": "#/components/schemas/Network"
          },
          "files_sealed": {
            "type": "boolean",
            "description": "Its files in storage are sealed with the account's own key."
          },
          "running_seconds": {
            "type": "number",
            "description": "All the time it has run, which is what is charged."
          },
          "disk_bytes": {
            "type": "integer"
          },
          "cpu_cores": {
            "type": "number",
            "description": "Processors it used, about a minute's average, when running."
          },
          "memory_bytes": {
            "type": "integer"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "last_used_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "ready_ms": {
            "type": "number",
            "description": "How long making it ready took, when this answer made it ready (create, start, copy)."
          }
        }
      },
      "GpuRef": {
        "type": "object",
        "required": [
          "id"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "An `id` from `GET /v1/gpus`."
          },
          "model": {
            "type": "string"
          },
          "count": {
            "type": "integer"
          },
          "memory_gb": {
            "type": "integer"
          }
        }
      },
      "MachineCreate": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "name": {
            "type": "string",
            "maxLength": 80,
            "description": "For people; ids are made by us."
          },
          "project": {
            "type": "string",
            "description": "Its project; the account's first project when left out."
          },
          "region": {
            "type": "string",
            "description": "A region `id` from `GET /v1/regions` to run near. Where it runs is said in `location`."
          },
          "gpu": {
            "type": "string",
            "description": "A GPU `id` from `GET /v1/gpus`. Needs the `cloud:gpus` scope."
          },
          "cpus": {
            "type": "integer",
            "enum": [
              2,
              4,
              8,
              16
            ],
            "default": 2,
            "description": "Its processors, with 2 GB of memory each: 16 is sixteen processors and 32 GB. Charged by the second at the\n2-processor price that many times over (16 is 8 times). Not with `gpu`: a GPU machine's size comes with it.\n"
          },
          "labels": {
            "type": "object",
            "maxProperties": 32,
            "additionalProperties": {
              "type": "string",
              "maxLength": 200
            }
          },
          "secrets": {
            "$ref": "#/components/schemas/SecretNames"
          },
          "network": {
            "allOf": [
              {
                "$ref": "#/components/schemas/NetworkSet"
              }
            ],
            "description": "Its internet policy, the outbound gate. Left out, the machine is open - it reaches the internet as every\nmachine always could. A machine made from a snapshot keeps the source machine's policy unless this sets\nits own.\n"
          },
          "from_snapshot": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "machine"
            ],
            "properties": {
              "machine": {
                "type": "string"
              },
              "snapshot": {
                "type": "string",
                "description": "A snapshot's id; its latest when left out."
              }
            }
          },
          "sleep_when_idle": {
            "type": "boolean",
            "default": true,
            "description": "True: it pauses by itself once it is idle - no call or connection has reached it for `idle_after_s`\nand its programs have been quiet as long (under 5% of one CPU on average), so a job left running keeps\nit going. Paused, it keeps its memory and no running time is charged. False: it never pauses by itself;\nit runs, and is charged, until it is paused or stopped.\n"
          },
          "idle_after_s": {
            "type": "integer",
            "minimum": 60,
            "maximum": 86400,
            "default": 300,
            "description": "How long it must be idle before it pauses by itself. Only with `sleep_when_idle` true."
          },
          "wait_s": {
            "type": "number",
            "minimum": 0,
            "maximum": 600,
            "default": 60
          }
        }
      },
      "MachineUpdate": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "name": {
            "type": "string",
            "maxLength": 80
          },
          "project": {
            "type": "string",
            "description": "Another of the account's projects to move it to, one this key reaches."
          },
          "labels": {
            "type": "object",
            "additionalProperties": {
              "type": [
                "string",
                "null"
              ],
              "maxLength": 200
            }
          },
          "secrets": {
            "allOf": [
              {
                "$ref": "#/components/schemas/SecretNames"
              }
            ],
            "description": "Every secret it is to get, said whole: the list takes the place of the one it had, and `[]` takes them all\naway (which needs no `cloud:secrets:write`: it gives nothing).\n"
          },
          "network": {
            "allOf": [
              {
                "$ref": "#/components/schemas/NetworkSet"
              }
            ],
            "description": "Its internet policy, said whole: it takes the place of the one it had. A running machine has its policy\nchanged on its host at once, without a restart.\n"
          }
        }
      },
      "Network": {
        "type": "object",
        "required": [
          "internet",
          "allow"
        ],
        "description": "A machine's internet policy - the outbound gate.",
        "properties": {
          "internet": {
            "type": "string",
            "enum": [
              "open",
              "closed",
              "allowed"
            ],
            "description": "`open`: it reaches the internet as every machine always could. `closed`: no outbound connections at all\n(its own server's model door and DNS aside). `allowed`: only what `allow` lists.\n"
          },
          "allow": {
            "type": "array",
            "description": "Where an `allowed` machine may reach; empty for `open` and `closed`.",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "NetworkSet": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "internet"
        ],
        "properties": {
          "internet": {
            "type": "string",
            "enum": [
              "open",
              "closed",
              "allowed"
            ]
          },
          "allow": {
            "type": "array",
            "maxItems": 64,
            "description": "For `allowed` only (empty or left out otherwise). Each entry is a domain name for HTTP and HTTPS - exact\n(`api.github.com`) or a single leading `*.` for every name under it (`*.githubusercontent.com`) - or an\nIPv4/IPv6 range as a CIDR (`140.82.112.0/20`) for any other port. HTTP and HTTPS reach only the listed\nnames (the gate reads the TLS SNI or the HTTP Host; a connection with no SNI or an unlisted one is\nrefused); other ports reach only the listed ranges.\n",
            "items": {
              "type": "string",
              "maxLength": 253
            }
          }
        }
      },
      "SecretNames": {
        "type": "array",
        "maxItems": 200,
        "uniqueItems": true,
        "description": "Names of the account's secrets that every command run in the machine gets as environment variables. Giving\nthem needs the `cloud:secrets:write` scope too, and a key of the whole account; each must be set already. A\ncommand can read its environment: any key that may run commands in the machine can read what it was given.\n",
        "items": {
          "type": "string",
          "pattern": "^[A-Z_][A-Z0-9_]{0,63}$"
        }
      },
      "MachineCopy": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "name": {
            "type": "string",
            "maxLength": 80
          },
          "labels": {
            "type": "object",
            "maxProperties": 32,
            "additionalProperties": {
              "type": "string",
              "maxLength": 200
            }
          },
          "wait_s": {
            "type": "number",
            "minimum": 0,
            "maximum": 600,
            "default": 60
          }
        }
      },
      "WaitOptions": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "wait_s": {
            "type": "number",
            "minimum": 0,
            "maximum": 600,
            "default": 60
          }
        }
      },
      "MachineList": {
        "type": "object",
        "required": [
          "object",
          "data",
          "next"
        ],
        "properties": {
          "object": {
            "const": "list"
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Machine"
            }
          },
          "next": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "Snapshot": {
        "type": "object",
        "required": [
          "id",
          "object",
          "machine",
          "created_at"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "object": {
            "const": "snapshot"
          },
          "machine": {
            "type": "string"
          },
          "note": {
            "type": "string"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "SnapshotList": {
        "type": "object",
        "required": [
          "object",
          "data"
        ],
        "properties": {
          "object": {
            "const": "list"
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Snapshot"
            }
          }
        }
      },
      "Folder": {
        "type": "object",
        "required": [
          "object",
          "machine",
          "path",
          "entries",
          "truncated"
        ],
        "properties": {
          "object": {
            "const": "folder"
          },
          "machine": {
            "type": "string"
          },
          "path": {
            "type": "string",
            "examples": [
              "home/notes"
            ]
          },
          "entries": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FolderEntry"
            }
          },
          "truncated": {
            "type": "boolean",
            "description": "True when it holds more than 1,000 and the list was cut there."
          }
        }
      },
      "FolderEntry": {
        "type": "object",
        "required": [
          "name",
          "type",
          "size",
          "modified_at"
        ],
        "properties": {
          "name": {
            "type": "string"
          },
          "type": {
            "type": "string",
            "enum": [
              "file",
              "folder",
              "link",
              "other"
            ]
          },
          "size": {
            "type": "integer",
            "description": "Bytes; 0 for a folder."
          },
          "modified_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "MachineMetrics": {
        "type": "object",
        "required": [
          "object",
          "machine",
          "step_s",
          "cpus",
          "kept_since",
          "points"
        ],
        "properties": {
          "object": {
            "const": "metrics"
          },
          "machine": {
            "type": "string"
          },
          "step_s": {
            "type": "integer",
            "description": "How long each point covers."
          },
          "cpus": {
            "type": [
              "number",
              "null"
            ],
            "description": "Its processors: what cpu_percent is a share of."
          },
          "kept_since": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "The oldest point its server keeps; null when it keeps none."
          },
          "points": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MetricsPoint"
            }
          }
        }
      },
      "MetricsPoint": {
        "type": "object",
        "required": [
          "at"
        ],
        "description": "A step of the machine. A number its server could not read is null (a machine of the microVM kind says its\nprocessors and memory alone), and so are the rates of a step its readings cover none of - the first after\nit woke.\n",
        "properties": {
          "at": {
            "type": "string",
            "format": "date-time",
            "description": "Where the step begins."
          },
          "cpu_percent": {
            "type": [
              "number",
              "null"
            ],
            "description": "Its processors' use, a share of its own size, 0 to 100."
          },
          "memory_bytes": {
            "type": [
              "number",
              "null"
            ],
            "description": "The most memory it held in the step."
          },
          "memory_limit_bytes": {
            "type": [
              "number",
              "null"
            ],
            "description": "The most memory it may hold."
          },
          "disk_read_bps": {
            "type": [
              "number",
              "null"
            ],
            "description": "Bytes a second read from its disk."
          },
          "disk_write_bps": {
            "type": [
              "number",
              "null"
            ],
            "description": "Bytes a second written to its disk."
          },
          "net_rx_bps": {
            "type": [
              "number",
              "null"
            ],
            "description": "Bytes a second received on its network."
          },
          "net_tx_bps": {
            "type": [
              "number",
              "null"
            ],
            "description": "Bytes a second sent on its network."
          }
        }
      },
      "ExecRequest": {
        "type": "object",
        "additionalProperties": false,
        "description": "Give `command` (the words, no shell) or `shell` (a line for `sh -lc`), not both.",
        "properties": {
          "command": {
            "type": "array",
            "minItems": 1,
            "maxItems": 4096,
            "items": {
              "type": "string"
            },
            "examples": [
              [
                "python3",
                "-c",
                "print(2+2)"
              ]
            ]
          },
          "shell": {
            "type": "string",
            "maxLength": 131072,
            "examples": [
              "ls -la | wc -l"
            ]
          },
          "env": {
            "type": "object",
            "maxProperties": 256,
            "additionalProperties": {
              "type": "string"
            },
            "description": "Environment variables for this command, beside the machine's secrets, which it gets as variables too. Of a\nname in both, the one given here wins: the secret is left out of this command, and the answer does not\nsay so. The command, its variables and the machine's secrets are at most 1 MiB together (413).\n"
          },
          "cwd": {
            "type": "string",
            "description": "Where it starts; the work folder when left out."
          },
          "user": {
            "type": "string",
            "enum": [
              "user",
              "root"
            ],
            "default": "user"
          },
          "binary_output": {
            "type": "boolean",
            "default": false,
            "description": "Give stdout as base64 in `stdout_base64`."
          }
        }
      },
      "ExecResult": {
        "type": "object",
        "required": [
          "exit_code",
          "stderr",
          "duration_ms",
          "truncated"
        ],
        "properties": {
          "exit_code": {
            "type": "integer"
          },
          "stdout": {
            "type": "string"
          },
          "stdout_base64": {
            "type": "string"
          },
          "stderr": {
            "type": "string"
          },
          "duration_ms": {
            "type": "number"
          },
          "truncated": {
            "type": "boolean",
            "description": "True when the output was longer than 8 MiB and was cut there."
          }
        }
      },
      "Port": {
        "type": "object",
        "required": [
          "object",
          "machine",
          "port",
          "public",
          "url",
          "created_at"
        ],
        "properties": {
          "object": {
            "const": "port"
          },
          "machine": {
            "type": "string"
          },
          "port": {
            "type": "integer",
            "minimum": 1,
            "maximum": 65535
          },
          "public": {
            "type": "boolean",
            "description": "True: anyone may reach the address. False: only a call with an API key that can see the machine, and a\nbrowser that opened a link made for it.\n"
          },
          "url": {
            "type": "string",
            "format": "uri",
            "examples": [
              "https://8080-01jb2xk3n4p5q6r7s8t9v0w1x2.run.wheelmachine.ai"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "PortOpen": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "port"
        ],
        "properties": {
          "port": {
            "type": "integer",
            "minimum": 1,
            "maximum": 65535
          },
          "public": {
            "type": "boolean",
            "default": false
          }
        }
      },
      "PortList": {
        "type": "object",
        "required": [
          "object",
          "data"
        ],
        "properties": {
          "object": {
            "const": "list"
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Port"
            }
          }
        }
      },
      "PortLinkCreate": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "access_s": {
            "type": "integer",
            "minimum": 60,
            "maximum": 3600,
            "default": 1800,
            "description": "How long the browser that opens the link may reach the address."
          },
          "path": {
            "type": "string",
            "maxLength": 2048,
            "default": "/",
            "description": "Where on the address the link lands: a path beginning with a single `/` (not `//` or `/\\`), with no space\nor backslash in it; a query may follow it.\n"
          }
        }
      },
      "PortLink": {
        "type": "object",
        "required": [
          "object",
          "machine",
          "port",
          "url",
          "expires_at",
          "access_s"
        ],
        "properties": {
          "object": {
            "const": "port_link"
          },
          "machine": {
            "type": "string"
          },
          "port": {
            "type": "integer",
            "minimum": 1,
            "maximum": 65535
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "Opens once. Shown in this answer only."
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the link stops opening, if it was not opened."
          },
          "access_s": {
            "type": "integer",
            "description": "How long the browser that opens it may reach the address."
          }
        }
      },
      "Secret": {
        "type": "object",
        "required": [
          "object",
          "name",
          "machines",
          "created_at",
          "updated_at"
        ],
        "description": "A secret as it is shown, always without its value.",
        "properties": {
          "object": {
            "const": "secret"
          },
          "name": {
            "type": "string",
            "examples": [
              "DATABASE_URL"
            ]
          },
          "machines": {
            "type": "array",
            "description": "The machines given it (those this key can see).",
            "items": {
              "type": "string"
            }
          },
          "outside": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/Outside"
              }
            ],
            "description": "Set when the secret is kept outside the machine: its value never enters the machine (its environment holds\nonly a placeholder) and the outbound gate puts it into a request header going to one of the listed hosts.\n"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "description": "When its value was last set."
          }
        }
      },
      "Outside": {
        "type": "object",
        "required": [
          "hosts",
          "header",
          "format"
        ],
        "description": "How a secret kept outside the machine is sent; never its value.",
        "properties": {
          "hosts": {
            "type": "array",
            "minItems": 1,
            "maxItems": 32,
            "description": "The hosts its value may be sent to (exact, or a leading `*.` for every name under one). When the machine\nis not `open`, each must also be allowed by its `network` policy.\n",
            "items": {
              "type": "string",
              "maxLength": 253
            }
          },
          "header": {
            "type": "string",
            "description": "The request header its value is written into, such as `Authorization`."
          },
          "format": {
            "type": "string",
            "description": "How the value is written, with `{value}` once; `Bearer {value}`, or `{value}` for the value alone."
          }
        }
      },
      "SecretList": {
        "type": "object",
        "required": [
          "object",
          "data"
        ],
        "properties": {
          "object": {
            "const": "list"
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Secret"
            }
          }
        }
      },
      "SecretSet": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "value"
        ],
        "properties": {
          "value": {
            "type": "string",
            "minLength": 1,
            "maxLength": 32768,
            "writeOnly": true,
            "description": "The value, up to 32 KiB of text with no NUL in it. It is never shown again."
          },
          "outside": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "hosts",
              "header"
            ],
            "description": "Keep the secret outside the machine: its value never enters the machine - not its environment (which holds\nonly a placeholder `wm_secret_<NAME>_<hex>`), its files, its process memory or any answer. The outbound\ngate on the host, which is given the value and the machine is not, puts it into the `header` of requests\ngoing to one of `hosts`. Left out, the value reaches the machine's commands as an environment variable.\n",
            "properties": {
              "hosts": {
                "type": "array",
                "minItems": 1,
                "maxItems": 32,
                "description": "Where its value may be sent (exact, or a leading `*.`). When the machine is not `open` these must also\nbe allowed by its `network` policy, or the gate could never reach them.\n",
                "items": {
                  "type": "string",
                  "maxLength": 253
                }
              },
              "header": {
                "type": "string",
                "maxLength": 64,
                "description": "The request header its value is written into, such as `Authorization`."
              },
              "format": {
                "type": "string",
                "maxLength": 256,
                "description": "How the value is written, with `{value}` exactly once; `{value}` (the value alone) by default."
              }
            }
          }
        }
      },
      "SSHCertificateRequest": {
        "type": "object",
        "required": [
          "public_key",
          "machine"
        ],
        "properties": {
          "public_key": {
            "type": "string",
            "description": "One line of an OpenSSH public key (ssh-ed25519 AAAA..., or ecdsa/rsa)."
          },
          "machine": {
            "type": "string",
            "description": "The machine's id; the certificate opens that machine alone."
          },
          "ttl_s": {
            "type": "integer",
            "minimum": 1,
            "maximum": 3600,
            "description": "How long it lives, in seconds; one hour at most, and the default."
          }
        }
      },
      "SSHDoor": {
        "type": "object",
        "required": [
          "object",
          "open",
          "ssh_host",
          "ca_public_key",
          "host_public_key"
        ],
        "properties": {
          "object": {
            "const": "ssh"
          },
          "open": {
            "type": "boolean",
            "description": "False until certificates are signed here and the server's address is set."
          },
          "ssh_host": {
            "type": [
              "string",
              "null"
            ],
            "description": "The address to reach the SSH server by."
          },
          "ca_public_key": {
            "type": "string",
            "description": "The authority's public key (@cert-authority); empty when none is set."
          },
          "host_public_key": {
            "type": "string",
            "description": "The SSH server's host key, to pin in known_hosts; empty when none is set."
          }
        }
      },
      "SSHCertificate": {
        "type": "object",
        "required": [
          "object",
          "certificate",
          "principal",
          "ssh_user",
          "expires_at"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "ssh_certificate"
            ]
          },
          "certificate": {
            "type": "string",
            "description": "The signed certificate, one OpenSSH line; save it next to the public key."
          },
          "principal": {
            "type": "string",
            "description": "The one principal: the machine's id."
          },
          "ssh_user": {
            "type": "string",
            "description": "The SSH user to connect as (the machine's id)."
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          },
          "ca_public_key": {
            "type": "string",
            "description": "The authority's public key, to trust the certificate (@cert-authority)."
          },
          "host_public_key": {
            "type": "string",
            "description": "The SSH server's host key, to pin in known_hosts."
          },
          "ssh_host": {
            "type": "string",
            "description": "The address to reach the SSH server by, when one is set."
          }
        }
      },
      "MachineSize": {
        "type": "object",
        "required": [
          "cpus",
          "memory_gb",
          "price_per_hour_usd"
        ],
        "properties": {
          "cpus": {
            "type": "integer",
            "enum": [
              2,
              4,
              8,
              16
            ]
          },
          "memory_gb": {
            "type": "integer"
          },
          "price_per_hour_usd": {
            "type": "number",
            "description": "What an hour of it running comes to."
          }
        }
      },
      "SizeList": {
        "type": "object",
        "required": [
          "object",
          "data"
        ],
        "properties": {
          "object": {
            "const": "list"
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MachineSize"
            }
          }
        }
      },
      "Region": {
        "type": "object",
        "required": [
          "id",
          "city",
          "country"
        ],
        "properties": {
          "id": {
            "type": "string",
            "examples": [
              "mumbai"
            ]
          },
          "city": {
            "type": "string"
          },
          "country": {
            "type": "string"
          }
        }
      },
      "GpuList": {
        "type": "object",
        "required": [
          "object",
          "data",
          "for_sale"
        ],
        "properties": {
          "object": {
            "const": "list"
          },
          "for_sale": {
            "type": "boolean",
            "description": "False while GPU machines are not yet open to every account; the list shows what is coming."
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/GpuOffer"
            }
          }
        }
      },
      "GpuOffer": {
        "type": "object",
        "required": [
          "id",
          "model",
          "count",
          "memory_gb",
          "price_per_hour_usd",
          "available"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "model": {
            "type": "string",
            "examples": [
              "H100 80GB"
            ]
          },
          "count": {
            "type": "integer"
          },
          "memory_gb": {
            "type": "integer"
          },
          "cpus": {
            "type": "integer"
          },
          "ram_gb": {
            "type": "number"
          },
          "disk_gb": {
            "type": "number"
          },
          "location": {
            "type": "object",
            "properties": {
              "city": {
                "type": "string"
              },
              "country": {
                "type": "string"
              }
            }
          },
          "price_per_hour_usd": {
            "type": "number"
          },
          "interruptible": {
            "type": "boolean",
            "description": "May be taken back at any time; cheaper for it."
          },
          "available": {
            "type": "boolean"
          }
        }
      },
      "Usage": {
        "type": "object",
        "required": [
          "object",
          "month",
          "machines",
          "total_usd"
        ],
        "properties": {
          "object": {
            "const": "usage"
          },
          "month": {
            "type": "string"
          },
          "total_usd": {
            "type": "number"
          },
          "machines": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "machine",
                "running_seconds",
                "usd"
              ],
              "properties": {
                "machine": {
                  "type": "string"
                },
                "project": {
                  "type": "string"
                },
                "running_seconds": {
                  "type": "number"
                },
                "usd": {
                  "type": "number"
                }
              }
            }
          }
        }
      },
      "Session": {
        "type": "object",
        "required": [
          "id",
          "object",
          "provider",
          "device",
          "address",
          "created_at",
          "last_seen_at",
          "expires_at",
          "current"
        ],
        "properties": {
          "id": {
            "type": "string",
            "examples": [
              "ses_01jb2xk3n4p5q6r7s8t9v0w1x2"
            ]
          },
          "object": {
            "const": "session"
          },
          "provider": {
            "type": "string",
            "enum": [
              "github",
              "google"
            ]
          },
          "device": {
            "type": "string",
            "description": "The browser it was made in, as a person names it.",
            "examples": [
              "Firefox on Windows"
            ]
          },
          "address": {
            "type": "string",
            "description": "Where it was made from."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "last_seen_at": {
            "type": "string",
            "format": "date-time"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          },
          "current": {
            "type": "boolean",
            "description": "The sign-in this call is made with."
          }
        }
      },
      "SessionList": {
        "type": "object",
        "required": [
          "object",
          "data"
        ],
        "properties": {
          "object": {
            "const": "list"
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Session"
            }
          }
        }
      },
      "DeviceLoginRequest": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "name": {
            "type": "string",
            "maxLength": 80,
            "description": "What the key will be called, such as `wm on laptop`."
          },
          "scopes": {
            "type": "array",
            "description": "What the program asks for; `cloud:*` when left out. The person may give it less.",
            "items": {
              "$ref": "#/components/schemas/Scope"
            }
          }
        }
      },
      "DeviceLoginStarted": {
        "type": "object",
        "required": [
          "object",
          "device_code",
          "user_code",
          "verification_uri",
          "expires_in",
          "expires_at",
          "interval"
        ],
        "properties": {
          "object": {
            "const": "device_login_start"
          },
          "device_code": {
            "type": "string",
            "description": "The program's own: never shown, never sent anywhere else."
          },
          "user_code": {
            "type": "string",
            "examples": [
              "BCDF-GHJK"
            ],
            "description": "What the person types at verification_uri, from their terminal."
          },
          "verification_uri": {
            "type": "string",
            "examples": [
              "https://app.wheelmachine.ai/device"
            ]
          },
          "expires_in": {
            "type": "integer",
            "description": "Seconds the codes last: 600."
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          },
          "interval": {
            "type": "integer",
            "description": "Seconds between two asks for the key."
          }
        }
      },
      "DeviceLoginToken": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "device_code"
        ],
        "properties": {
          "device_code": {
            "type": "string"
          }
        }
      },
      "DeviceLoginPending": {
        "type": "object",
        "required": [
          "object",
          "state",
          "interval",
          "expires_at"
        ],
        "properties": {
          "object": {
            "const": "device_login"
          },
          "state": {
            "const": "pending"
          },
          "interval": {
            "type": "integer"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "DeviceLoginDecision": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "user_code"
        ],
        "properties": {
          "user_code": {
            "type": "string",
            "description": "As the person typed it; case, spaces and dashes do not matter."
          },
          "scopes": {
            "type": "array",
            "description": "Approving only - what the program is given, within what it asked for.",
            "items": {
              "$ref": "#/components/schemas/Scope"
            }
          }
        }
      },
      "DeviceLogin": {
        "type": "object",
        "required": [
          "id",
          "object",
          "name",
          "scopes",
          "state",
          "address",
          "device",
          "created_at",
          "expires_at"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "object": {
            "const": "device_login"
          },
          "name": {
            "type": "string"
          },
          "scopes": {
            "type": "array",
            "description": "What it asked for; once decided, what it was given.",
            "items": {
              "$ref": "#/components/schemas/Scope"
            }
          },
          "state": {
            "type": "string",
            "enum": [
              "pending",
              "approved",
              "denied",
              "done"
            ]
          },
          "address": {
            "type": "string",
            "description": "Where the program asked from: compare it with where you are."
          },
          "device": {
            "type": "string",
            "examples": [
              "wm 1.0.0 on Linux"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Providers": {
        "type": "object",
        "required": [
          "object",
          "github",
          "google"
        ],
        "properties": {
          "object": {
            "const": "providers"
          },
          "github": {
            "type": "boolean"
          },
          "google": {
            "type": "boolean"
          }
        }
      }
    }
  }
}
