{
  "openapi": "3.1.0",
  "info": {
    "title": "Innstance API",
    "version": "1.1.0",
    "description": "The Innstance control-plane API: publish static sites, deploy and run\napps on machines, host assistants, connect custom domains, and manage\nplans and payments. The `innstance` CLI (`ifhost` is its former name;\nit is installed beside it and still works) and the dashboard are\nclients of exactly this API.\n\nThis file is served live at `/openapi.json` and `/openapi.yaml`, with\n`servers` naming the control plane that serves it. Every build checks\nit against the server's router: every mounted API route is described\nhere, and nothing is described that the server does not mount. Routes\nthat are not part of the API (admin, pages, static assets, the payment\nprovider's webhook) are left out on purpose. Some routes exist only\nwhile their feature is configured (sites, assistants, Clerk sign-in,\nthe deposit rail, trust and safety); their descriptions say so.\n\nRequest and response shapes follow what the server writes and what the\nCLI reads. Where a shape varies by condition, every variant is\ndocumented; clients rely only on the documented fields.\n\nErrors: every JSON error body is the envelope\n`{\"error\", \"code\", \"retryable\", \"docs_url\"}` (the `Error` schema).\nBranch on `code` and `retryable`, never on the sentence. The catalog\nof codes, with what each means and what to do, is `/docs/errors`\n(`/docs/errors.json`, `/docs/errors.md`), which also lists the CLI's\nexit codes. Agents: `/llms.txt` is the short guide and\n`/llms-full.txt` is everything in one file.\n\nPlan ids are API identifiers: `team` is shown as \"Studio\" in current\ncopy. Hosted \"agents\" are called \"assistants\" in current copy; the\n`/agents` routes and the CLI's `agents` commands keep their names.\n\nEndpoints are grouped into two contracts:\n  - **Frontend surface** (`x-audience: frontend`) — the contract the\n    web dashboard consumes. Do not change these shapes without\n    coordinating with the dashboard.\n  - **Agent surface** (`x-audience: agent` / `cli`) — exec, console,\n    machines, runner deploys, uploads. Consumed by the CLI and coding\n    agents.\n\nThe public endpoints (`security: []`) need no bearer token and are\ntagged `Public`, `Docs` or `Distribution`.\n",
    "contact": {
      "name": "Innstance",
      "url": "https://innstance.impossibuild.ai/docs"
    }
  },
  "servers": [
    {
      "url": "https://innstance.impossibuild.ai",
      "description": "Production API"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "tags": [
    {
      "name": "Public",
      "description": "No authentication required."
    },
    {
      "name": "Auth",
      "description": "Session, API tokens, and account identity."
    },
    {
      "name": "Apps",
      "description": "App lifecycle (create/read/update/delete/apply)."
    },
    {
      "name": "Deployments",
      "description": "Deployment history and build-log streaming."
    },
    {
      "name": "Env",
      "description": "Plaintext environment variables."
    },
    {
      "name": "Secrets",
      "description": "Encrypted-at-rest secret values."
    },
    {
      "name": "Volumes",
      "description": "Persistent disk storage."
    },
    {
      "name": "Domains",
      "description": "Custom domain + TLS."
    },
    {
      "name": "Logs",
      "description": "Application log streaming."
    },
    {
      "name": "Health",
      "description": "Machine + HTTP health for an app."
    },
    {
      "name": "Billing",
      "description": "Plan limits, usage metering, spend alerts."
    },
    {
      "name": "Subscription",
      "description": "Card/crypto subscription management."
    },
    {
      "name": "Topup",
      "description": "Pay-as-you-go USDC top-ups (x402)."
    },
    {
      "name": "Wallet",
      "description": "Wallet address bindings for the deposit-address rail."
    },
    {
      "name": "Machines (Agent)",
      "description": "Machine lifecycle — start/stop/restart/list/destroy."
    },
    {
      "name": "Exec (Agent)",
      "description": "One-shot command execution inside a running machine."
    },
    {
      "name": "Console (Agent)",
      "description": "tmux-backed interactive console sessions."
    },
    {
      "name": "Sites",
      "description": "Static publishing — HTML, PDFs and images, no machine."
    },
    {
      "name": "Assistants",
      "description": "Hosted assistants from the catalog (the `/agents` routes; `innstance agents` in the CLI)."
    },
    {
      "name": "Docs",
      "description": "Machine-readable documentation — this description, the error catalog, the agent guides."
    },
    {
      "name": "Distribution",
      "description": "The CLI installers and their signatures."
    },
    {
      "name": "Abuse",
      "description": "Reporting a hosted address."
    }
  ],
  "paths": {
    "/health": {
      "get": {
        "tags": [
          "Public"
        ],
        "summary": "Platform health check",
        "security": [],
        "x-audience": "public",
        "responses": {
          "200": {
            "description": "Store reachable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HealthOK"
                }
              }
            }
          },
          "503": {
            "description": "Store unreachable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HealthError"
                }
              }
            }
          }
        }
      }
    },
    "/cli/version": {
      "get": {
        "tags": [
          "Public"
        ],
        "summary": "Return the signed CLI update manifest",
        "description": "Returns the embedded CLI release's build ID (which changes only when\nthe CLI's own sources do, not with every deployment), a SHA-256 and\nsame-origin `/dl/` archive URL for each embedded platform, and an\nEd25519 signature over the manifest. This endpoint does not redirect to GitHub Release\nmetadata or `latest.json`.\n",
        "security": [],
        "x-audience": "public",
        "responses": {
          "200": {
            "description": "Signed update manifest",
            "headers": {
              "Cache-Control": {
                "description": "Always `no-store` to prevent downgrade replays",
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "build_id",
                    "downloads",
                    "signature"
                  ],
                  "properties": {
                    "build_id": {
                      "type": "string"
                    },
                    "downloads": {
                      "type": "object",
                      "additionalProperties": {
                        "type": "object",
                        "required": [
                          "url",
                          "sha256"
                        ],
                        "properties": {
                          "url": {
                            "type": "string",
                            "pattern": "^/dl/"
                          },
                          "sha256": {
                            "type": "string",
                            "pattern": "^[0-9a-f]{64}$"
                          }
                        }
                      }
                    },
                    "signature": {
                      "type": "string",
                      "contentEncoding": "base64"
                    }
                  }
                }
              }
            }
          },
          "503": {
            "description": "Release signing key unavailable or invalid",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/dl/release.txt": {
      "get": {
        "tags": [
          "Public"
        ],
        "summary": "Return the installer release record",
        "description": "Shell-parseable provenance and SHA-256 entries generated from the\narchives embedded in the current API image.\n",
        "security": [],
        "x-audience": "public",
        "responses": {
          "200": {
            "description": "Embedded release record",
            "headers": {
              "Cache-Control": {
                "description": "Shared caches may retain this immutable deployment artifact for one hour; clients revalidate",
                "schema": {
                  "type": "string",
                  "const": "public, max-age=0, s-maxage=3600, must-revalidate"
                }
              }
            },
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "404": {
            "description": "No release record was embedded"
          }
        }
      }
    },
    "/dl/release.txt.sshsig": {
      "get": {
        "tags": [
          "Public"
        ],
        "summary": "Return the installer release-record signature",
        "description": "SSHSIG armor over the exact `/dl/release.txt` bytes, signed with the\ntrust anchor also used for `/cli/version`.\n",
        "security": [],
        "x-audience": "public",
        "responses": {
          "200": {
            "description": "SSHSIG-armored release signature",
            "headers": {
              "Cache-Control": {
                "description": "Shared caches may retain this immutable deployment artifact for one hour; clients revalidate",
                "schema": {
                  "type": "string",
                  "const": "public, max-age=0, s-maxage=3600, must-revalidate"
                }
              }
            },
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "404": {
            "description": "Release signing is unavailable"
          }
        }
      }
    },
    "/dl/{filename}": {
      "get": {
        "tags": [
          "Public"
        ],
        "summary": "Download a current CLI archive or checksum",
        "description": "Serves the platform archives embedded in the current API image and\ngenerated `.sha256` sidecars. This is the active client update\nchannel; it does not read GitHub Release assets or metadata.\n",
        "security": [],
        "x-audience": "public",
        "parameters": [
          {
            "name": "filename",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Embedded archive or generated SHA-256 sidecar",
            "headers": {
              "Cache-Control": {
                "description": "Shared caches may retain this immutable deployment artifact for one hour; clients revalidate",
                "schema": {
                  "type": "string",
                  "const": "public, max-age=0, s-maxage=3600, must-revalidate"
                }
              }
            },
            "content": {
              "application/gzip": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "application/zip": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "404": {
            "description": "The requested release file is not embedded"
          }
        }
      }
    },
    "/install": {
      "get": {
        "tags": [
          "Distribution"
        ],
        "summary": "The macOS and Linux installer script",
        "description": "A POSIX shell script that downloads the CLI archive for this machine\nfrom `/dl/`, verifies `/dl/release.txt` against its SSHSIG and the\narchive against the record, and installs `innstance` with `ifhost`\nbeside it. Inspect it before running it; `/install.sshsig` signs\nthese exact bytes.\n",
        "security": [],
        "x-audience": "public",
        "responses": {
          "200": {
            "description": "Installer script",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "500": {
            "description": "The embedded installer is missing from this build",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/install.sshsig": {
      "get": {
        "tags": [
          "Distribution"
        ],
        "summary": "SSHSIG signature over the exact `/install` bytes",
        "security": [],
        "x-audience": "public",
        "responses": {
          "200": {
            "description": "SSHSIG-armored signature",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "404": {
            "description": "Release signing is not configured on this server"
          }
        }
      }
    },
    "/install.ps1": {
      "get": {
        "tags": [
          "Distribution"
        ],
        "summary": "The Windows PowerShell installer script",
        "security": [],
        "x-audience": "public",
        "responses": {
          "200": {
            "description": "Installer script",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "500": {
            "description": "The embedded installer is missing from this build",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/install.ps1.sshsig": {
      "get": {
        "tags": [
          "Distribution"
        ],
        "summary": "SSHSIG signature over the exact `/install.ps1` bytes",
        "security": [],
        "x-audience": "public",
        "responses": {
          "200": {
            "description": "SSHSIG-armored signature",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "404": {
            "description": "Release signing is not configured on this server"
          }
        }
      }
    },
    "/skills": {
      "get": {
        "tags": [
          "Docs"
        ],
        "summary": "Redirect to the current agent skill (SKILL.md)",
        "description": "Follows the skill repository's protected main branch, so an approved\nskill update is live here without a platform release. `no-store`, so\nno agent keeps an older guide.\n",
        "security": [],
        "x-audience": "public",
        "responses": {
          "302": {
            "description": "Redirect to the raw SKILL.md",
            "headers": {
              "Location": {
                "schema": {
                  "type": "string",
                  "format": "uri"
                }
              },
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            }
          }
        }
      }
    },
    "/llms.txt": {
      "get": {
        "tags": [
          "Docs"
        ],
        "summary": "The agent guide (llmstxt.org), with the live price list",
        "description": "The short guide an agent reads first: install, sign-in, the core\ndeploy and publish flows, the CLI commands and the REST routes, with\nthe current plans and prices filled in from the catalog the billing\ncode uses. `/llms-full.txt` has everything in one file.\n",
        "security": [],
        "x-audience": "public",
        "responses": {
          "200": {
            "description": "Agent guide",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/llm.txt": {
      "get": {
        "tags": [
          "Docs"
        ],
        "summary": "The agent guide at its first address (same as /llms.txt)",
        "deprecated": true,
        "security": [],
        "x-audience": "public",
        "responses": {
          "200": {
            "description": "Agent guide",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/llms-full.txt": {
      "get": {
        "tags": [
          "Docs"
        ],
        "summary": "The whole documentation in one file",
        "description": "The agent guide, the CLI guide (`/docs`), the advanced guide\n(`/docs/advanced`), an index of every route in this description and\nthe error catalog with the CLI's exit codes, generated by the server\nfrom the sources those pages are served from.\n",
        "security": [],
        "x-audience": "public",
        "responses": {
          "200": {
            "description": "Full documentation (Markdown, served as text)",
            "headers": {
              "ETag": {
                "schema": {
                  "type": "string"
                }
              },
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "public, max-age=300, must-revalidate"
                }
              }
            },
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "304": {
            "description": "Not modified (If-None-Match matched the ETag)"
          }
        }
      }
    },
    "/openapi.json": {
      "get": {
        "tags": [
          "Docs"
        ],
        "summary": "This API description, as JSON",
        "description": "This document (OpenAPI 3.1), with `servers` naming the control plane\nthat serves it. Readable cross-origin, so browser-based API tools\ncan load it.\n",
        "security": [],
        "x-audience": "public",
        "responses": {
          "200": {
            "description": "OpenAPI document",
            "headers": {
              "ETag": {
                "schema": {
                  "type": "string"
                }
              },
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "public, max-age=300, must-revalidate"
                }
              },
              "Access-Control-Allow-Origin": {
                "schema": {
                  "type": "string",
                  "const": "*"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "304": {
            "description": "Not modified (If-None-Match matched the ETag)"
          }
        }
      }
    },
    "/openapi.yaml": {
      "get": {
        "tags": [
          "Docs"
        ],
        "summary": "This API description, as YAML",
        "security": [],
        "x-audience": "public",
        "responses": {
          "200": {
            "description": "OpenAPI document",
            "headers": {
              "ETag": {
                "schema": {
                  "type": "string"
                }
              },
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "public, max-age=300, must-revalidate"
                }
              },
              "Access-Control-Allow-Origin": {
                "schema": {
                  "type": "string",
                  "const": "*"
                }
              }
            },
            "content": {
              "application/yaml": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "304": {
            "description": "Not modified (If-None-Match matched the ETag)"
          }
        }
      }
    },
    "/docs/errors.json": {
      "get": {
        "tags": [
          "Docs"
        ],
        "summary": "The error catalog, for machines",
        "description": "Every code the API sends in an error body: its HTTP statuses,\nwhether the same request can succeed if sent again, what happened,\nwhat to do, and its `docs_url`. Also the CLI's exit codes. The same\ncatalog is a page at `/docs/errors` and Markdown at `/docs/errors.md`.\n",
        "security": [],
        "x-audience": "public",
        "responses": {
          "200": {
            "description": "Error catalog",
            "headers": {
              "ETag": {
                "schema": {
                  "type": "string"
                }
              },
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "public, max-age=300, must-revalidate"
                }
              },
              "Access-Control-Allow-Origin": {
                "schema": {
                  "type": "string",
                  "const": "*"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "docs_url": {
                      "type": "string",
                      "format": "uri"
                    },
                    "envelope": {
                      "type": "string",
                      "description": "The error envelope, in words."
                    },
                    "errors": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ErrorCatalogEntry"
                      }
                    },
                    "cli_exit_codes": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "code": {
                            "type": "string",
                            "example": "1"
                          },
                          "meaning": {
                            "type": "string"
                          }
                        },
                        "required": [
                          "code",
                          "meaning"
                        ]
                      }
                    }
                  },
                  "required": [
                    "docs_url",
                    "envelope",
                    "errors",
                    "cli_exit_codes"
                  ]
                }
              }
            }
          },
          "304": {
            "description": "Not modified (If-None-Match matched the ETag)"
          }
        }
      }
    },
    "/docs/errors.md": {
      "get": {
        "tags": [
          "Docs"
        ],
        "summary": "The error catalog and CLI exit codes, as Markdown",
        "security": [],
        "x-audience": "public",
        "responses": {
          "200": {
            "description": "Error catalog",
            "headers": {
              "ETag": {
                "schema": {
                  "type": "string"
                }
              },
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "public, max-age=300, must-revalidate"
                }
              }
            },
            "content": {
              "text/markdown": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "304": {
            "description": "Not modified (If-None-Match matched the ETag)"
          }
        }
      }
    },
    "/.well-known/security.txt": {
      "get": {
        "tags": [
          "Docs"
        ],
        "summary": "Security contact (RFC 9116)",
        "security": [],
        "x-audience": "public",
        "responses": {
          "200": {
            "description": "security.txt",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/regions": {
      "get": {
        "tags": [
          "Public"
        ],
        "summary": "Deploy regions",
        "description": "Needed while writing `impossible.toml`, which happens before the caller necessarily has a token.",
        "security": [],
        "x-audience": "public",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "regions": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "code": {
                            "type": "string",
                            "example": "iad"
                          },
                          "name": {
                            "type": "string",
                            "example": "Ashburn, Virginia (US)"
                          },
                          "area": {
                            "type": "string"
                          }
                        },
                        "required": [
                          "code",
                          "name",
                          "area"
                        ]
                      }
                    }
                  },
                  "required": [
                    "regions"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/billing/plans": {
      "get": {
        "tags": [
          "Public",
          "Billing"
        ],
        "summary": "The public price list",
        "description": "Every plan with its price and limits, from the catalog the billing\ncode enforces. Plan ids are API identifiers: `team` is shown as\n\"Studio\" in current copy. A limit that has no ceiling is `null` with\n`\u003ckey\u003e_unlimited: true` beside it.\n",
        "security": [],
        "x-audience": "public",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "plans": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string",
                            "enum": [
                              "free",
                              "hobby",
                              "pro",
                              "team"
                            ]
                          },
                          "name": {
                            "type": "string"
                          },
                          "price_cents": {
                            "type": "integer"
                          },
                          "price_display": {
                            "type": "string",
                            "example": "$15/mo"
                          },
                          "limits": {
                            "$ref": "#/components/schemas/PlanLimits"
                          }
                        },
                        "required": [
                          "id",
                          "name",
                          "price_cents",
                          "price_display",
                          "limits"
                        ]
                      }
                    }
                  },
                  "required": [
                    "plans"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/qr": {
      "get": {
        "tags": [
          "Public"
        ],
        "summary": "QR code PNG for a hosted address",
        "description": "Public (an `\u003cimg\u003e` cannot send a bearer header), limited per address,\nand only for HTTPS links on the tenant domain and its subdomains.\n",
        "security": [],
        "x-audience": "frontend",
        "parameters": [
          {
            "name": "text",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "example": "https://my-site.innstance.app"
            }
          },
          {
            "name": "size",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 64,
              "maximum": 1024,
              "default": 256
            }
          }
        ],
        "responses": {
          "200": {
            "description": "PNG image",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "public, max-age=3600"
                }
              }
            },
            "content": {
              "image/png": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/abuse/reports": {
      "post": {
        "tags": [
          "Public",
          "Abuse"
        ],
        "summary": "Report a hosted site, app or assistant",
        "description": "The public report intake behind `/abuse`. No account needed; strict\nJSON, limited per caller and per network, with fleet-wide ceilings.\nMounted only while trust and safety is on.\n",
        "security": [],
        "x-audience": "public",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "url": {
                    "type": "string",
                    "format": "uri",
                    "description": "The hosted address being reported."
                  },
                  "category": {
                    "type": "string",
                    "description": "One of the categories the /abuse form offers."
                  },
                  "details": {
                    "type": "string"
                  },
                  "reporter_email": {
                    "type": "string",
                    "format": "email"
                  },
                  "reporter_name": {
                    "type": "string"
                  },
                  "copyright": {
                    "type": "object",
                    "description": "Required for copyright reports (the notice's statements)."
                  }
                },
                "required": [
                  "url",
                  "category"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Report recorded",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "id",
                    "status"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "413": {
            "description": "Body over the report size cap (plain text)",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/auth/google/web": {
      "get": {
        "tags": [
          "Public",
          "Auth"
        ],
        "summary": "Browser login — redirects to Google's consent screen",
        "security": [],
        "x-audience": "public",
        "responses": {
          "302": {
            "description": "Redirect to accounts.google.com"
          },
          "503": {
            "description": "Interactive sign-in not configured on this server. NOTE: written via\n`http.Error`, so the body is `text/plain`, not the JSON error\nenvelope used everywhere else.\n",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/auth/callback": {
      "get": {
        "tags": [
          "Public",
          "Auth"
        ],
        "summary": "Google redirects here after consent (browser flow)",
        "security": [],
        "x-audience": "public",
        "parameters": [
          {
            "name": "code",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "302": {
            "description": "Sets an HttpOnly API session cookie and redirects to\n`/dashboard#email=\u003cemail\u003e` on success; the token is never exposed\nto the URL or JavaScript.\n"
          },
          "400": {
            "description": "Missing code parameter (text/plain via http.Error)",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "403": {
            "description": "Account not usable for sign-in (unverified email) (text/plain via http.Error)",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "502": {
            "description": "Google token exchange failed (text/plain via http.Error)",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/auth/clerk/exchange": {
      "post": {
        "tags": [
          "Public",
          "Auth"
        ],
        "summary": "Exchange a Clerk browser session for an Innstance web session",
        "security": [],
        "x-audience": "frontend",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "token": {
                    "type": "string",
                    "description": "Clerk session token"
                  }
                },
                "required": [
                  "token"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Web session cookies set; response names the authenticated email",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "email": {
                      "type": "string",
                      "format": "email"
                    }
                  },
                  "required": [
                    "email"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Missing Clerk session token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Clerk session or account rejected",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Email is unverified, new sign-ups are unavailable, or a new identity needs terms acceptance",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "The verified email belongs to an existing account, but this immutable Clerk ID has not been explicitly linked",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/auth/clerk/accept": {
      "post": {
        "tags": [
          "Public",
          "Auth"
        ],
        "summary": "Create a parked Clerk account after terms acceptance",
        "security": [],
        "x-audience": "frontend",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "signup_token": {
                    "type": "string",
                    "pattern": "^[0-9a-f]{48}$"
                  },
                  "terms_accepted": {
                    "type": "boolean",
                    "const": true
                  },
                  "utm": {
                    "type": "object",
                    "description": "Optional. The campaign the sign-up page's own address\ncarried, used only to attribute the new account's signup.\nOnly these three keys are read; each value is reduced to\nlowercase letters, digits, dots, underscores and dashes,\nat most 40 bytes. It never refuses the sign-up: any other\nkey, a value that is not a string, or a `utm` that is not\nan object is ignored.\n",
                    "properties": {
                      "utm_source": {
                        "type": "string"
                      },
                      "utm_medium": {
                        "type": "string"
                      },
                      "utm_campaign": {
                        "type": "string"
                      }
                    }
                  }
                },
                "required": [
                  "signup_token",
                  "terms_accepted"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Account created and web session cookies set",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "email": {
                      "type": "string",
                      "format": "email"
                    }
                  },
                  "required": [
                    "email"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Signup token missing, expired, spent, or terms not accepted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "New sign-ups are unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/auth/clerk/link": {
      "post": {
        "tags": [
          "Auth"
        ],
        "summary": "Explicitly link a Clerk identity to the authenticated account",
        "description": "Requires an existing Innstance session and a Clerk session whose verified,\nnormalized email matches that account. Linking is atomic and immutable;\nnormal login never links by email.\n",
        "x-audience": "frontend",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "token": {
                    "type": "string",
                    "description": "Clerk session token for the identity being linked"
                  }
                },
                "required": [
                  "token"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Identity linked",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string",
                      "const": "linked"
                    },
                    "provider": {
                      "type": "string",
                      "const": "clerk"
                    }
                  },
                  "required": [
                    "status",
                    "provider"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Existing Innstance session or Clerk session rejected",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Provider email is unverified or does not match the authenticated account",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Provider ID belongs to another user or the account already has a different immutable Clerk ID",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/auth/google/link": {
      "post": {
        "tags": [
          "Auth"
        ],
        "summary": "Explicitly link a Google identity to the authenticated account",
        "description": "Requires an existing Innstance session and a Google access token whose\nverified, normalized email matches that account. Linking is atomic and\nimmutable; normal login never links by email.\n",
        "x-audience": "frontend",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "access_token": {
                    "type": "string",
                    "description": "Google access token for the identity being linked"
                  }
                },
                "required": [
                  "access_token"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Identity linked",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string",
                      "const": "linked"
                    },
                    "provider": {
                      "type": "string",
                      "const": "google"
                    }
                  },
                  "required": [
                    "status",
                    "provider"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Existing Innstance session or Google identity rejected",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Provider email is unverified or does not match the authenticated account",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Provider ID belongs to another user or the account already has a different immutable Google ID",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/auth/device/start": {
      "post": {
        "tags": [
          "Public",
          "Auth"
        ],
        "summary": "Start a CLI device-authorization grant",
        "description": "The CLI retains `device_code`; show `user_code` to the human at `verification_url`.",
        "security": [],
        "x-audience": "cli",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "device_name": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Pending device grant",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "device_code": {
                      "type": "string"
                    },
                    "user_code": {
                      "type": "string",
                      "example": "ABCD-EFGH"
                    },
                    "verification_url": {
                      "type": "string",
                      "format": "uri"
                    },
                    "interval": {
                      "type": "integer",
                      "example": 5
                    },
                    "expires_in": {
                      "type": "integer",
                      "example": 600
                    }
                  },
                  "required": [
                    "device_code",
                    "user_code",
                    "verification_url",
                    "interval",
                    "expires_in"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Invalid request body",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/auth/device/lookup": {
      "get": {
        "tags": [
          "Public",
          "Auth"
        ],
        "summary": "Resolve a human device code for the approval page",
        "security": [],
        "x-audience": "frontend",
        "parameters": [
          {
            "name": "user_code",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "example": "ABCD-EFGH"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Pending device grant",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "user_code": {
                      "type": "string"
                    },
                    "device_name": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "pending"
                      ]
                    }
                  },
                  "required": [
                    "user_code",
                    "device_name",
                    "status"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Missing or malformed user code",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Device code absent, expired, or already resolved",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/auth/device/approve": {
      "post": {
        "tags": [
          "Public",
          "Auth"
        ],
        "summary": "Approve or deny a CLI device grant from the browser",
        "security": [],
        "x-audience": "frontend",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "user_code": {
                    "type": "string",
                    "example": "ABCD-EFGH"
                  },
                  "clerk_token": {
                    "type": "string",
                    "description": "Required for approval; not required when action is deny."
                  },
                  "action": {
                    "type": "string",
                    "enum": [
                      "deny"
                    ]
                  },
                  "signup_token": {
                    "type": "string",
                    "pattern": "^[0-9a-f]{48}$"
                  },
                  "terms_accepted": {
                    "type": "boolean",
                    "const": true
                  }
                },
                "required": [
                  "user_code"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Grant approved or denied. Approval token remains server-side for the CLI poll.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string",
                      "enum": [
                        "approved",
                        "denied"
                      ]
                    }
                  },
                  "required": [
                    "status"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Clerk session rejected",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Email is unverified or a new identity must accept terms",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Device code absent, expired, or already resolved",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Another approval or denial atomically decided this grant, or the provider identity requires explicit account linking",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/auth/device/poll": {
      "post": {
        "tags": [
          "Public",
          "Auth"
        ],
        "summary": "Poll a CLI device-authorization grant",
        "security": [],
        "x-audience": "cli",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "device_code": {
                    "type": "string"
                  }
                },
                "required": [
                  "device_code"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Denied grant or one-time approved token hand-off",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string",
                      "enum": [
                        "approved",
                        "denied"
                      ]
                    },
                    "token": {
                      "type": "string",
                      "description": "Present exactly once when approved."
                    },
                    "email": {
                      "type": "string",
                      "format": "email"
                    }
                  },
                  "required": [
                    "status"
                  ]
                }
              }
            }
          },
          "202": {
            "description": "Grant remains pending",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string",
                      "enum": [
                        "pending"
                      ]
                    }
                  },
                  "required": [
                    "status"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Missing device code",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "Grant expired or approved token already collected",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/auth/google/cli-config": {
      "get": {
        "tags": [
          "Public",
          "Auth"
        ],
        "summary": "Legacy public Desktop OAuth client_id for loopback+PKCE CLI login",
        "deprecated": true,
        "security": [],
        "x-audience": "public",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "client_id": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "client_id"
                  ]
                }
              }
            }
          },
          "503": {
            "description": "Legacy CLI login not configured",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/auth/google/cli": {
      "post": {
        "tags": [
          "Public",
          "Auth"
        ],
        "summary": "Legacy exchange of a CLI-obtained Google auth code for an Innstance session token",
        "deprecated": true,
        "description": "The CLI runs the browser + loopback + PKCE leg itself, then hands the\nresulting code + verifier + redirect_uri here. The server holds the\nGoogle Desktop client_secret and performs the code exchange — the\nsecret never ships to user machines.\n",
        "security": [],
        "x-audience": "public",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "code": {
                    "type": "string"
                  },
                  "code_verifier": {
                    "type": "string"
                  },
                  "redirect_uri": {
                    "type": "string",
                    "example": "http://127.0.0.1:54321/callback"
                  },
                  "signup_token": {
                    "type": "string",
                    "pattern": "^[0-9a-f]{48}$",
                    "description": "One-time token returned when the first exchange identifies a new account."
                  },
                  "terms_accepted": {
                    "type": "boolean",
                    "const": true
                  }
                },
                "oneOf": [
                  {
                    "required": [
                      "code",
                      "code_verifier",
                      "redirect_uri"
                    ]
                  },
                  {
                    "required": [
                      "signup_token",
                      "terms_accepted"
                    ]
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Session token issued",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "token": {
                      "type": "string"
                    },
                    "email": {
                      "type": "string",
                      "format": "email"
                    },
                    "username": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "authenticated"
                      ]
                    }
                  },
                  "required": [
                    "token",
                    "email",
                    "username",
                    "status"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Missing fields, sign-in exchange failed, or pending signup token expired/already used",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Account not usable for sign-in, or a new verified account\nrequires legal acceptance. The latter returns `terms_required`,\n`signup_token`, and `terms_url`; resubmit the one-time token with\n`terms_accepted: true` to finish account creation.\n",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string"
                        },
                        "terms_required": {
                          "type": "boolean",
                          "const": true
                        },
                        "signup_token": {
                          "type": "string",
                          "pattern": "^[0-9a-f]{48}$"
                        },
                        "terms_url": {
                          "type": "string",
                          "format": "uri"
                        }
                      },
                      "required": [
                        "error",
                        "terms_required",
                        "signup_token",
                        "terms_url"
                      ]
                    }
                  ]
                }
              }
            }
          },
          "503": {
            "description": "Legacy CLI login not configured",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/auth/me": {
      "get": {
        "tags": [
          "Auth"
        ],
        "summary": "Current user identity + plan + over-pool state",
        "x-audience": "frontend",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Me"
                }
              }
            }
          },
          "404": {
            "description": "User not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/auth/token": {
      "post": {
        "tags": [
          "Auth"
        ],
        "summary": "Create an additional API token",
        "description": "The secret is returned once. Send an `Idempotency-Key` (a lowercase\nUUID v4) to make a retry safe: a second request with the same key\nanswers 409 `TOKEN_REQUEST_REPLAY` instead of minting another token.\n",
        "x-audience": "frontend",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "description": "Defaults to \"api-token\" if omitted/blank (\"mcp\" when mcp is true)."
                  },
                  "expires_in_days": {
                    "type": "integer",
                    "minimum": 0,
                    "description": "Days until expiry, 0 = never (permanent tokens are capped per account, max 365 days otherwise). Defaults to 90, or 30 when mcp is true. A token for MCP always expires."
                  },
                  "scopes": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "enum": [
                        "read",
                        "publish",
                        "deploy",
                        "destroy",
                        "domains",
                        "billing:read"
                      ]
                    },
                    "description": "Limits the token to these scopes. Omitted (and no apps or sites) is a full-access token, exactly as before scopes existed. Sent, it must name at least one scope; an empty list is refused, never read as full access."
                  },
                  "apps": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Limits the token to these apps (and the named sites). At most 20."
                  },
                  "sites": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Limits the token to these sites (and the named apps). At most 20."
                  },
                  "mcp": {
                    "type": "boolean",
                    "description": "A token for an assistant's MCP client: defaults to every scope the MCP tools use (all but destroy) and 30 days, and the response carries mcp_url."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Token created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "token": {
                      "type": "string"
                    },
                    "id": {
                      "type": "string"
                    },
                    "name": {
                      "type": "string"
                    },
                    "access": {
                      "type": "string",
                      "description": "\"full access\", or the scopes and names that limit the token."
                    },
                    "expires_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "scopes": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "apps": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "sites": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "mcp_url": {
                      "type": "string",
                      "description": "Where to point an MCP client (only when mcp was true)."
                    }
                  },
                  "required": [
                    "token",
                    "id",
                    "name"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Unknown scope, invalid name, a scopes/apps/sites list that was sent but names nothing, or an MCP token asked never to expire",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The caller is itself a limited token; only a full-access credential can create tokens",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "TOKEN_REQUEST_REPLAY, or a token limit is reached (CONFLICT)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/auth/tokens": {
      "get": {
        "tags": [
          "Auth"
        ],
        "summary": "List all API tokens for the current user",
        "x-audience": "frontend",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "tokens": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/TokenSummary"
                      }
                    }
                  },
                  "required": [
                    "tokens"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/auth/tokens/{tokenId}": {
      "delete": {
        "tags": [
          "Auth"
        ],
        "summary": "Revoke an API token",
        "x-audience": "frontend",
        "parameters": [
          {
            "$ref": "#/components/parameters/TokenId"
          }
        ],
        "responses": {
          "200": {
            "description": "Revoked",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "revoked": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "revoked"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Token not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/auth/logout": {
      "post": {
        "tags": [
          "Auth"
        ],
        "summary": "End the session that authenticated this call (`innstance logout`)",
        "description": "Revokes session credentials only. Called with a user-created API\ntoken it revokes nothing and says so: that token may be live in CI\nor on other machines; revoke it with `DELETE /auth/tokens/{tokenId}`.\n",
        "x-audience": "cli",
        "responses": {
          "200": {
            "description": "Revoked, or why nothing was",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "revoked": {
                      "type": "boolean"
                    },
                    "reason": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "revoked"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/auth/web/logout": {
      "post": {
        "tags": [
          "Public",
          "Auth"
        ],
        "summary": "End the browser session (clears the session cookies)",
        "description": "Needs the same-origin CSRF header a cookie-authenticated write sends.",
        "security": [],
        "x-audience": "frontend",
        "responses": {
          "204": {
            "description": "Signed out"
          },
          "403": {
            "description": "Cross-origin request rejected (FORBIDDEN)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/apps": {
      "get": {
        "tags": [
          "Apps"
        ],
        "summary": "List apps owned by the current user",
        "x-audience": "frontend",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "apps": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/AppSummary"
                      }
                    }
                  },
                  "required": [
                    "apps"
                  ]
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Apps"
        ],
        "summary": "Create an app",
        "x-audience": "frontend",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AppCreateRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AppCreateResponse"
                }
              }
            }
          },
          "400": {
            "description": "Validation failure (bad name/region/cpu_kind/memory_mb/cpus/autoscale/storage/mount_path)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "Account over its plan pool",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OverPoolError"
                }
              }
            }
          },
          "403": {
            "description": "Tier limit violation (e.g. APP_LIMIT, FREE_TIER_DISABLED)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TierViolationError"
                }
              }
            }
          },
          "409": {
            "description": "Pool write contention, or the app name is already taken",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/PoolLockedError"
                    },
                    {
                      "$ref": "#/components/schemas/Error"
                    }
                  ]
                }
              }
            }
          },
          "500": {
            "description": "Internal error (e.g. acquiring the pool lock)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "Upstream app-create or network-provisioning failure. When the provider's\nanswer was lost the code is OUTCOME_UNKNOWN: the app may exist, and the\nattempt stays listed (GET /apps) — do not send the same create again.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "The app was created but its record could not be saved (OUTCOME_UNKNOWN); it stays listed for recovery",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/apps/{name}": {
      "get": {
        "tags": [
          "Apps"
        ],
        "summary": "Get app details",
        "x-audience": "frontend",
        "parameters": [
          {
            "$ref": "#/components/parameters/AppName"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AppDetail"
                }
              }
            }
          },
          "404": {
            "description": "App not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "patch": {
        "tags": [
          "Apps"
        ],
        "summary": "Update app config (cpu/memory/port/autoscale/concurrency)",
        "description": "Config-only: persists the new spec to the store but does NOT push it\nto running machines. `applied_at: \"next_deploy\"` signals that a\n`POST /apps/{name}/apply` (or a redeploy) is still required to roll\nthe change out.\n",
        "x-audience": "frontend",
        "parameters": [
          {
            "$ref": "#/components/parameters/AppName"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AppUpdateRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "updated": {
                      "type": "string"
                    },
                    "applied_at": {
                      "type": "string",
                      "enum": [
                        "next_deploy"
                      ]
                    }
                  },
                  "required": [
                    "updated",
                    "applied_at"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Validation failure, or no fields supplied",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "Account over its plan pool",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OverPoolError"
                }
              }
            }
          },
          "403": {
            "description": "Tier limit violation",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TierViolationError"
                }
              }
            }
          },
          "404": {
            "description": "App not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Pool write contention",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PoolLockedError"
                }
              }
            }
          },
          "500": {
            "description": "Internal error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Apps"
        ],
        "summary": "Delete an app (and its supplier resources)",
        "x-audience": "frontend",
        "parameters": [
          {
            "$ref": "#/components/parameters/AppName"
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "deleted": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "deleted"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "App not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "Upstream deletion failed. The app row is kept with\n`status: \"delete_failed\"`; retry by calling DELETE again.\n",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "code": {
                      "type": "string",
                      "enum": [
                        "DELETE_FAILED"
                      ]
                    },
                    "retry": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error",
                    "code",
                    "retry"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/apps/{name}/apply": {
      "post": {
        "tags": [
          "Apps"
        ],
        "summary": "Push the app's current stored config to every running machine",
        "description": "Rebuilds each machine's Guest spec (cpu/memory) and env from the\nstore and pushes it via an update call, retrying 3× on the\nsupplier's machine-replacement race. Does not redeploy the image.\n",
        "x-audience": "frontend",
        "parameters": [
          {
            "$ref": "#/components/parameters/AppName"
          }
        ],
        "responses": {
          "200": {
            "description": "Every machine applied successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApplyResult"
                }
              }
            }
          },
          "207": {
            "description": "Some machines failed — see `failed_machines`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApplyResult"
                }
              }
            }
          },
          "404": {
            "description": "App not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Would grow always-on machines past what the plan covers (ALWAYS_ON_COST_LIMIT / ALWAYS_ON_PERFORMANCE); no machine was touched",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TierViolationError"
                }
              }
            }
          },
          "502": {
            "description": "Could not list machines",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "The always-on check could not read what it needs for a change that could add always-on cost (LIMITS_UNAVAILABLE); nothing changed, retry",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/apps/{name}/deployments": {
      "get": {
        "tags": [
          "Deployments"
        ],
        "summary": "List recent deployments (newest first, max 20)",
        "x-audience": "frontend",
        "parameters": [
          {
            "$ref": "#/components/parameters/AppName"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "deployments": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/DeploymentSummary"
                      }
                    }
                  },
                  "required": [
                    "deployments"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "App not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/apps/{name}/deployments/{id}": {
      "get": {
        "tags": [
          "Deployments"
        ],
        "summary": "Get a single deployment's status (for async deploy polling)",
        "x-audience": "frontend",
        "parameters": [
          {
            "$ref": "#/components/parameters/AppName"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeploymentDetail"
                }
              }
            }
          },
          "404": {
            "description": "App or deployment not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/apps/{name}/deployments/{id}/stream": {
      "get": {
        "tags": [
          "Deployments"
        ],
        "summary": "Stream build logs for a deployment (Server-Sent Events)",
        "x-audience": "frontend",
        "parameters": [
          {
            "$ref": "#/components/parameters/AppName"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`text/event-stream`. Each build-log line is sent as\n`data: \u003cline\u003e\\n\\n`; the stream ends with\n`event: status\\ndata: \u003cdeployment status\u003e\\n\\n`.\n",
            "content": {
              "text/event-stream": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "404": {
            "description": "App or deployment not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/apps/{name}/env": {
      "get": {
        "tags": [
          "Env"
        ],
        "summary": "List plaintext env vars",
        "x-audience": "frontend",
        "parameters": [
          {
            "$ref": "#/components/parameters/AppName"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "vars": {
                      "type": "object",
                      "additionalProperties": {
                        "type": "string"
                      }
                    }
                  },
                  "required": [
                    "vars"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "App not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "put": {
        "tags": [
          "Env"
        ],
        "summary": "Set (merge) env vars",
        "description": "Merge semantics — existing keys not present in the request are\nuntouched. Restarts the machine to apply, unless `?no_restart=true`.\nCapped at 100 vars per app (existing ∪ incoming).\n",
        "x-audience": "frontend",
        "parameters": [
          {
            "$ref": "#/components/parameters/AppName"
          },
          {
            "$ref": "#/components/parameters/NoRestart"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "vars": {
                    "type": "object",
                    "additionalProperties": {
                      "type": "string"
                    }
                  }
                },
                "required": [
                  "vars"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SetResult"
                }
              }
            }
          },
          "400": {
            "description": "Missing vars, invalid key name, or over the 100-var cap",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "App not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/apps/{name}/env/{key}": {
      "delete": {
        "tags": [
          "Env"
        ],
        "summary": "Remove one env var",
        "x-audience": "frontend",
        "parameters": [
          {
            "$ref": "#/components/parameters/AppName"
          },
          {
            "name": "key",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/NoRestart"
          }
        ],
        "responses": {
          "200": {
            "description": "Removed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RemoveResult"
                }
              }
            }
          },
          "404": {
            "description": "App not found, or the key was never set",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/apps/{name}/secrets": {
      "get": {
        "tags": [
          "Secrets"
        ],
        "summary": "List secret keys (values are never returned)",
        "x-audience": "frontend",
        "parameters": [
          {
            "$ref": "#/components/parameters/AppName"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "keys": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  },
                  "required": [
                    "keys"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "App not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "put": {
        "tags": [
          "Secrets"
        ],
        "summary": "Set (merge) secrets — encrypted at rest",
        "description": "Same merge/restart/cap semantics as `PUT /apps/{name}/env`, applied\nto the encrypted secrets store (max 100 per app).\n",
        "x-audience": "frontend",
        "parameters": [
          {
            "$ref": "#/components/parameters/AppName"
          },
          {
            "$ref": "#/components/parameters/NoRestart"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "secrets": {
                    "type": "object",
                    "additionalProperties": {
                      "type": "string"
                    }
                  }
                },
                "required": [
                  "secrets"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SetResult"
                }
              }
            }
          },
          "400": {
            "description": "Missing secrets, invalid key name, or over the 100-secret cap",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "App not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal error (query or encrypt failure)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/apps/{name}/secrets/{key}": {
      "delete": {
        "tags": [
          "Secrets"
        ],
        "summary": "Remove one secret",
        "x-audience": "frontend",
        "parameters": [
          {
            "$ref": "#/components/parameters/AppName"
          },
          {
            "name": "key",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/NoRestart"
          }
        ],
        "responses": {
          "200": {
            "description": "Removed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RemoveResult"
                }
              }
            }
          },
          "404": {
            "description": "App not found, or the key was never set",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/apps/{name}/volumes": {
      "get": {
        "tags": [
          "Volumes"
        ],
        "summary": "List volumes (replica clones from scale-up are hidden)",
        "x-audience": "frontend",
        "parameters": [
          {
            "$ref": "#/components/parameters/AppName"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "volumes": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Volume"
                      }
                    }
                  },
                  "required": [
                    "volumes"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "App not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Volumes"
        ],
        "summary": "Create + attach a volume",
        "description": "Rejected with a validation error if the app's `autoscale_max \u003e 1`\n(local volumes are per-machine — \"one volume = one machine\").\n",
        "x-audience": "frontend",
        "parameters": [
          {
            "$ref": "#/components/parameters/AppName"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "description": "Lowercase letters, digits, underscores; ≤40 chars."
                  },
                  "size_gb": {
                    "type": "integer",
                    "default": 1,
                    "minimum": 1
                  },
                  "mount_path": {
                    "type": "string",
                    "description": "Absolute path; must not be a system directory."
                  }
                },
                "required": [
                  "name",
                  "mount_path"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Volume"
                }
              }
            }
          },
          "400": {
            "description": "Validation failure (name/mount_path/autoscale rule)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "Account over its plan pool",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OverPoolError"
                }
              }
            }
          },
          "403": {
            "description": "Tier limit violation (VOLUMES_NOT_AVAILABLE / POOL_VOLUME_LIMIT)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TierViolationError"
                }
              }
            }
          },
          "404": {
            "description": "App not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Pool write contention",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PoolLockedError"
                }
              }
            }
          },
          "500": {
            "description": "Internal error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "Upstream volume-create failure",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/apps/{name}/volumes/{volName}/extend": {
      "put": {
        "tags": [
          "Volumes"
        ],
        "summary": "Grow a volume (never shrink)",
        "x-audience": "frontend",
        "parameters": [
          {
            "$ref": "#/components/parameters/AppName"
          },
          {
            "$ref": "#/components/parameters/VolName"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "size_gb": {
                    "type": "integer",
                    "minimum": 1
                  }
                },
                "required": [
                  "size_gb"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Extended (or a no-op 200 if `size_gb` equals the current size —\nidempotent).\n",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "name": {
                      "type": "string"
                    },
                    "old_gb": {
                      "type": "integer"
                    },
                    "new_gb": {
                      "type": "integer"
                    }
                  },
                  "required": [
                    "name",
                    "old_gb",
                    "new_gb"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Missing/invalid size_gb, or size_gb smaller than current",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Would exceed the plan's volume pool",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "App or volume not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal error (including the partial-failure case where the disk grew but the store update failed)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "Upstream extend failure",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/apps/{name}/volumes/{volName}": {
      "delete": {
        "tags": [
          "Volumes"
        ],
        "summary": "Delete a volume",
        "x-audience": "frontend",
        "parameters": [
          {
            "$ref": "#/components/parameters/AppName"
          },
          {
            "$ref": "#/components/parameters/VolName"
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "deleted": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "deleted"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "App or volume not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/apps/{name}/domains": {
      "get": {
        "tags": [
          "Domains"
        ],
        "summary": "List domains (including the auto-provisioned default hostname)",
        "x-audience": "frontend",
        "parameters": [
          {
            "$ref": "#/components/parameters/AppName"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "domains": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Domain"
                      }
                    }
                  },
                  "required": [
                    "domains"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "App not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Domains"
        ],
        "summary": "Add a custom domain (claims it once ownership is proven, then provisions TLS)",
        "description": "Nothing is written until the caller proves DNS control: a TXT record at\n`_innstance-verify.\u003chostname\u003e` whose value is returned in the 428 body.\nRe-POST to re-check; the server answers 201 once the record is visible.\nUntil then it answers 428 (a 4xx so clients that treat 2xx as \"added\"\nprint the instruction instead) with the record to publish.\n",
        "x-audience": "frontend",
        "parameters": [
          {
            "$ref": "#/components/parameters/AppName"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "hostname": {
                    "type": "string",
                    "description": "Lowercase RFC 1123 DNS name, ≥1 dot, no wildcards, ≤253 chars."
                  }
                },
                "required": [
                  "hostname"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Ownership proven; hostname claimed and certificate requested",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "hostname": {
                      "type": "string"
                    },
                    "verified": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "tls_status": {
                      "type": "string"
                    },
                    "cname_target": {
                      "type": "string",
                      "description": "Point the custom domain's CNAME here."
                    }
                  },
                  "required": [
                    "hostname",
                    "verified",
                    "tls_status",
                    "cname_target"
                  ]
                }
              }
            }
          },
          "428": {
            "description": "Proof record not visible yet; nothing was written",
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "integer"
                },
                "description": "Seconds before the next check is admitted"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string",
                      "description": "Human instruction naming the record to add"
                    },
                    "code": {
                      "type": "string",
                      "enum": [
                        "DOMAIN_PROOF_REQUIRED"
                      ]
                    },
                    "hostname": {
                      "type": "string"
                    },
                    "verified": {
                      "type": "boolean",
                      "enum": [
                        false
                      ]
                    },
                    "verify": {
                      "type": "object",
                      "description": "The DNS record to publish, then re-POST.",
                      "properties": {
                        "type": {
                          "type": "string",
                          "enum": [
                            "TXT"
                          ]
                        },
                        "name": {
                          "type": "string",
                          "example": "_innstance-verify.www.example.com"
                        },
                        "value": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "type",
                        "name",
                        "value"
                      ]
                    },
                    "provider": {
                      "type": "object",
                      "description": "The DNS panel to open, detected from the zone's nameservers; url is empty when unrecognised.",
                      "properties": {
                        "key": {
                          "type": "string",
                          "example": "cloudflare"
                        },
                        "label": {
                          "type": "string",
                          "example": "Cloudflare"
                        },
                        "url": {
                          "type": "string",
                          "example": "https://dash.cloudflare.com"
                        }
                      }
                    },
                    "records": {
                      "type": "object",
                      "description": "The whole plan, known before anything is claimed: a CNAME from the\nhostname to the base domain (an ALIAS where apex is true, since a bare\ndomain cannot hold a CNAME) and the proof TXT. No address appears; the\ncustomer's DNS names a hostname the platform owns.\n",
                      "properties": {
                        "cname": {
                          "type": "object",
                          "properties": {
                            "name": {
                              "type": "string",
                              "example": "www.example.com"
                            },
                            "target": {
                              "type": "string",
                              "example": "innstance.impossibuild.ai"
                            },
                            "apex": {
                              "type": "boolean"
                            }
                          }
                        },
                        "verify": {
                          "type": "object",
                          "properties": {
                            "name": {
                              "type": "string"
                            },
                            "value": {
                              "type": "string"
                            }
                          }
                        }
                      }
                    },
                    "retry_after_seconds": {
                      "type": "integer"
                    }
                  },
                  "required": [
                    "error",
                    "code",
                    "hostname",
                    "verified",
                    "verify",
                    "provider",
                    "retry_after_seconds"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Missing/invalid hostname, or a platform hostname",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Tier limit violation (DOMAINS_NOT_AVAILABLE / DOMAIN_LIMIT)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TierViolationError"
                }
              }
            }
          },
          "404": {
            "description": "App not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Hostname already registered to another app",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Re-checked inside the verification window; honour Retry-After",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "Certificate provisioning failed, or DNS could not be queried",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Domain verification is not configured on this server",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/apps/{name}/domains/{hostname}": {
      "delete": {
        "tags": [
          "Domains"
        ],
        "summary": "Remove a custom domain",
        "x-audience": "frontend",
        "parameters": [
          {
            "$ref": "#/components/parameters/AppName"
          },
          {
            "name": "hostname",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Removed",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "removed": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "removed"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "App not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "Certificate deletion failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/apps/{name}/domains/{hostname}/status": {
      "get": {
        "tags": [
          "Domains"
        ],
        "summary": "Certificate progress and the DNS plan for an app's custom domain",
        "description": "Asks the certificate provider to re-check (at most once per window;\n`nudge=false` skips it) and reports what it says. Rows connected\nthrough the edge report the CNAME plan; rows from before it keep the\naddress plan their certificate was issued against.\n",
        "x-audience": "frontend",
        "parameters": [
          {
            "$ref": "#/components/parameters/AppName"
          },
          {
            "$ref": "#/components/parameters/Hostname"
          },
          {
            "$ref": "#/components/parameters/Nudge"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "hostname": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string",
                      "description": "The certificate provider's status."
                    },
                    "configured": {
                      "type": "boolean",
                      "description": "HTTPS is serving."
                    },
                    "dns_provider": {
                      "type": "string"
                    },
                    "provider": {
                      "$ref": "#/components/schemas/DNSProvider"
                    },
                    "rate_limited_until": {
                      "type": "string"
                    },
                    "validation": {
                      "type": "object"
                    },
                    "validation_errors": {
                      "type": [
                        "array",
                        "null"
                      ],
                      "items": {
                        "type": "object"
                      }
                    },
                    "records": {
                      "type": "object",
                      "description": "The CNAME plan (DomainRecords) for edge rows; `a`, `aaaa`, `ownership` and `verify` for rows from before the edge."
                    }
                  },
                  "required": [
                    "hostname",
                    "status",
                    "configured",
                    "records"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "502": {
            "$ref": "#/components/responses/UpstreamError"
          }
        }
      }
    },
    "/sites/check-name": {
      "get": {
        "tags": [
          "Sites"
        ],
        "summary": "Check whether a name is free (advisory; the claim is POST /sites)",
        "description": "Public and limited per address, because names are global. With a\ncredential, a name the caller already holds is described as theirs,\nand `hold` says how many more names the caller may lock. Always 200:\n`available` and `reason` carry the answer.\n",
        "security": [
          {},
          {
            "bearerAuth": []
          }
        ],
        "x-audience": "frontend",
        "parameters": [
          {
            "name": "name",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The answer",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "name": {
                      "type": "string"
                    },
                    "available": {
                      "type": "boolean"
                    },
                    "reason": {
                      "type": "string",
                      "description": "Why not, in words; empty when available."
                    },
                    "code": {
                      "type": "string",
                      "enum": [
                        "NAME_NOT_ALLOWED"
                      ],
                      "description": "Present when the name policy refuses the name."
                    },
                    "url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The address the name would get."
                    },
                    "hold": {
                      "$ref": "#/components/schemas/SiteHold"
                    }
                  },
                  "required": [
                    "name",
                    "available",
                    "reason"
                  ]
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/sites": {
      "get": {
        "tags": [
          "Sites"
        ],
        "summary": "List this account's sites",
        "x-audience": "frontend",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "sites": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Site"
                      }
                    },
                    "hold": {
                      "$ref": "#/components/schemas/SiteHold"
                    }
                  },
                  "required": [
                    "sites",
                    "hold"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "post": {
        "tags": [
          "Sites"
        ],
        "summary": "Claim a site name (files come next, with PUT /sites/{name}/files)",
        "description": "Atomically claims the name. Claiming a name the caller already holds\nwith nothing published is a retry and answers the same 201. A new\nname passes the name policy (400 `NAME_NOT_ALLOWED`) and the cap on\nnames held with nothing published (409 `SETUP_LIMIT`).\n",
        "x-audience": "frontend",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "mode": {
                    "type": "string",
                    "enum": [
                      "website",
                      "files"
                    ],
                    "description": "`website` is exactly one HTML file, served at /; `files` is one or more PDFs and/or images with an index at / (several PDFs are combined into one document)."
                  }
                },
                "required": [
                  "name",
                  "mode"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Claimed",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "name": {
                      "type": "string"
                    },
                    "mode": {
                      "type": "string"
                    },
                    "url": {
                      "type": "string",
                      "format": "uri"
                    },
                    "hold": {
                      "$ref": "#/components/schemas/SiteHold"
                    },
                    "expires_at": {
                      "type": "string",
                      "format": "date-time",
                      "description": "Free plan only — when the site pauses."
                    }
                  },
                  "required": [
                    "name",
                    "mode",
                    "url"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Plan limit (SITE_LIMIT, with upgrade), or the account is under review (UNDER_REVIEW)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Name taken (CONFLICT or NAME_TAKEN), too many unfinished names (SETUP_LIMIT), or another change in progress (OPERATION_IN_PROGRESS)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/sites/{name}": {
      "delete": {
        "tags": [
          "Sites"
        ],
        "summary": "Remove a site (files first, then the record)",
        "x-audience": "frontend",
        "parameters": [
          {
            "$ref": "#/components/parameters/SiteName"
          }
        ],
        "responses": {
          "200": {
            "description": "Removed",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "removed": {
                      "type": "string"
                    },
                    "note": {
                      "type": "string",
                      "description": "What happens to the name now."
                    }
                  },
                  "required": [
                    "removed",
                    "note"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Held for review (UNDER_REVIEW)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          },
          "502": {
            "$ref": "#/components/responses/UpstreamError"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/sites/{name}/files": {
      "put": {
        "tags": [
          "Sites"
        ],
        "summary": "Publish files to a claimed site (replaces the whole site)",
        "description": "Multipart form: the files under the field name `files`, and\noptionally `mode` to switch the site's kind. Every upload is checked\nbefore it goes live: a held upload answers 403 `UNDER_REVIEW` with\n`held: true`. The free plan publishes web file types only (422\n`FILE_TYPE_NOT_ALLOWED`); an image the check cannot read is 422\n`IMAGE_NOT_CHECKABLE`. Refusals that published nothing carry\n`upload_rejected: true`.\n",
        "x-audience": "frontend",
        "parameters": [
          {
            "$ref": "#/components/parameters/SiteName"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "properties": {
                  "files": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "binary"
                    }
                  },
                  "mode": {
                    "type": "string",
                    "enum": [
                      "website",
                      "files"
                    ]
                  }
                },
                "required": [
                  "files"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Published",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "url": {
                      "type": "string",
                      "format": "uri"
                    },
                    "live": {
                      "type": "boolean",
                      "description": "Whether the address answered right after publishing."
                    },
                    "file_count": {
                      "type": "integer"
                    },
                    "size_bytes": {
                      "type": "integer"
                    },
                    "merged": {
                      "type": "boolean",
                      "description": "PDFs were combined into one document."
                    },
                    "notices": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "expires_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "trial_note": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "url",
                    "live",
                    "file_count",
                    "size_bytes",
                    "merged"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Trial ended (TRIAL_EXPIRED), or held for review (UNDER_REVIEW)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "413": {
            "description": "Over the plan's site size",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "FILE_TYPE_NOT_ALLOWED or IMAGE_NOT_CHECKABLE",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "502": {
            "$ref": "#/components/responses/UpstreamError"
          }
        }
      }
    },
    "/sites/{name}/archive": {
      "put": {
        "tags": [
          "Sites"
        ],
        "summary": "Publish a built website from one .zip (replaces the whole site)",
        "description": "Multipart form: the site as one .zip under the field name `archive`,\nand optionally `spa` (`auto`, `on` or `off`). The site must have\n`index.html` at its top. It is checked as PUT /sites/{name}/files is,\nfile by file: a held upload answers 403 `UNDER_REVIEW` with\n`held: true`. Refusals that published nothing carry\n`upload_rejected: true`. Answers 503 while folder publishing is off.\n",
        "x-audience": "agent",
        "parameters": [
          {
            "$ref": "#/components/parameters/SiteName"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "properties": {
                  "archive": {
                    "type": "string",
                    "format": "binary"
                  },
                  "spa": {
                    "type": "string",
                    "enum": [
                      "auto",
                      "on",
                      "off"
                    ]
                  }
                },
                "required": [
                  "archive"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Published",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "url": {
                      "type": "string"
                    },
                    "live": {
                      "type": "boolean"
                    },
                    "file_count": {
                      "type": "integer"
                    },
                    "size_bytes": {
                      "type": "integer"
                    },
                    "notices": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Held for review, or a free site past its trial",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Site not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Larger than the plan allows",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/sites/{name}/versions": {
      "get": {
        "tags": [
          "Sites"
        ],
        "summary": "List the versions kept for a site, newest first, and which is live",
        "x-audience": "agent",
        "parameters": [
          {
            "$ref": "#/components/parameters/SiteName"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "name": {
                      "type": "string"
                    },
                    "url": {
                      "type": "string"
                    },
                    "live": {
                      "type": "string",
                      "description": "The live version's ID; empty when none is recorded."
                    },
                    "versions": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      }
                    },
                    "note": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "Site not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/sites/{name}/versions/{id}": {
      "delete": {
        "tags": [
          "Sites"
        ],
        "summary": "Remove one kept version that is not live",
        "x-audience": "agent",
        "parameters": [
          {
            "$ref": "#/components/parameters/SiteName"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Removed",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "name": {
                      "type": "string"
                    },
                    "deleted": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "Site or version not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/sites/{name}/rollback": {
      "post": {
        "tags": [
          "Sites"
        ],
        "summary": "Make an earlier version live",
        "description": "With no `version`, returns to the version that was live before the\ncurrent one was published. A version must fit the current plan and\nhave been checked by rules at least as strict as the plan's now;\notherwise 409 says why and the live version is unchanged.\n",
        "x-audience": "agent",
        "parameters": [
          {
            "$ref": "#/components/parameters/SiteName"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "version": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Rolled back",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "Site or version not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "The version cannot be restored, or another change holds the site",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/sites/{name}/upload-sessions": {
      "post": {
        "tags": [
          "Sites"
        ],
        "summary": "Start an upload session for a caller that cannot send multipart",
        "description": "Names and sizes in, one signed PUT URL per file out. Answers 503\nwhile upload sessions are off. See docs/static-sites.md.\n",
        "x-audience": "agent",
        "parameters": [
          {
            "$ref": "#/components/parameters/SiteName"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "mode": {
                    "type": "string"
                  },
                  "files": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "name": {
                          "type": "string"
                        },
                        "size": {
                          "type": "integer"
                        }
                      }
                    }
                  }
                },
                "required": [
                  "files"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Session created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "413": {
            "description": "Larger than one upload session allows",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/sites/{name}/upload-sessions/{id}/finalize": {
      "post": {
        "tags": [
          "Sites"
        ],
        "summary": "Publish a session's staged files, as PUT /sites/{name}/files does",
        "x-audience": "agent",
        "parameters": [
          {
            "$ref": "#/components/parameters/SiteName"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Published",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "409": {
            "description": "Files are missing, or another change holds the site",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/upload-sessions/{id}/files/{index}": {
      "put": {
        "tags": [
          "Sites"
        ],
        "summary": "Upload one file of a session to its signed URL",
        "description": "The signature in the URL is the credential; no token is needed. Each URL takes one upload, one request at a time, up to the declared size.",
        "security": [],
        "x-audience": "agent",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "index",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/octet-stream": {
              "schema": {
                "type": "string",
                "format": "binary"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Stored",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "received": {
                      "type": "integer"
                    },
                    "name": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/mcp": {
      "post": {
        "tags": [
          "Auth"
        ],
        "summary": "Remote MCP endpoint for assistants",
        "description": "Speaks the Model Context Protocol over streamable HTTP; each tool is\nserved through this API as the caller. Answers 503 while MCP is off.\n",
        "x-audience": "agent",
        "responses": {
          "200": {
            "description": "MCP response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/sites/{name}/domains": {
      "get": {
        "tags": [
          "Domains"
        ],
        "summary": "List a site's custom domains",
        "x-audience": "frontend",
        "parameters": [
          {
            "$ref": "#/components/parameters/SiteName"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "domains": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/SiteDomain"
                      }
                    }
                  },
                  "required": [
                    "domains"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "post": {
        "tags": [
          "Domains"
        ],
        "summary": "Add a custom domain to a site (same proof flow as apps)",
        "description": "Exactly the app flow (`POST /apps/{name}/domains`): 428\n`DOMAIN_PROOF_REQUIRED` with the TXT record to publish until it is\nvisible, then 201 once claimed. The customer's DNS is a CNAME to the\nbase domain (an ALIAS at a bare domain); no address is ever given.\n",
        "x-audience": "frontend",
        "parameters": [
          {
            "$ref": "#/components/parameters/SiteName"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "hostname": {
                    "type": "string"
                  }
                },
                "required": [
                  "hostname"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Ownership proven; hostname claimed and certificate requested",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "hostname": {
                      "type": "string"
                    },
                    "verified": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "tls_status": {
                      "type": "string"
                    },
                    "cname_target": {
                      "type": "string"
                    },
                    "records": {
                      "$ref": "#/components/schemas/DomainRecords"
                    }
                  },
                  "required": [
                    "hostname",
                    "verified",
                    "tls_status",
                    "cname_target",
                    "records"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Tier limit (DOMAINS_NOT_AVAILABLE / DOMAIN_LIMIT)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TierViolationError"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "428": {
            "description": "Proof record not visible yet (DOMAIN_PROOF_REQUIRED); same body as the app route",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "502": {
            "$ref": "#/components/responses/UpstreamError"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/sites/{name}/domains/{hostname}/status": {
      "get": {
        "tags": [
          "Domains"
        ],
        "summary": "Certificate progress and the DNS plan for a site's custom domain",
        "x-audience": "frontend",
        "parameters": [
          {
            "$ref": "#/components/parameters/SiteName"
          },
          {
            "$ref": "#/components/parameters/Hostname"
          },
          {
            "$ref": "#/components/parameters/Nudge"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "hostname": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string",
                      "description": "The certificate provider's status."
                    },
                    "configured": {
                      "type": "boolean",
                      "description": "HTTPS is serving."
                    },
                    "dns_provider": {
                      "type": "string"
                    },
                    "provider": {
                      "$ref": "#/components/schemas/DNSProvider"
                    },
                    "records": {
                      "$ref": "#/components/schemas/DomainRecords"
                    }
                  },
                  "required": [
                    "hostname",
                    "status",
                    "configured",
                    "records"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "502": {
            "$ref": "#/components/responses/UpstreamError"
          }
        }
      }
    },
    "/sites/{name}/domains/{hostname}": {
      "delete": {
        "tags": [
          "Domains"
        ],
        "summary": "Disconnect a custom domain from a site",
        "x-audience": "frontend",
        "parameters": [
          {
            "$ref": "#/components/parameters/SiteName"
          },
          {
            "$ref": "#/components/parameters/Hostname"
          }
        ],
        "responses": {
          "200": {
            "description": "Removed",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "removed": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "removed"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "502": {
            "$ref": "#/components/responses/UpstreamError"
          }
        }
      }
    },
    "/apps/{name}/logs": {
      "get": {
        "tags": [
          "Logs"
        ],
        "summary": "Stream (or fetch historical) app logs",
        "x-audience": "frontend",
        "parameters": [
          {
            "$ref": "#/components/parameters/AppName"
          },
          {
            "name": "format",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "json"
              ]
            },
            "description": "`format=json` emits one JSON object per line instead of the plain-text `HH:MM:SS.mmm [region] message` format."
          },
          {
            "name": "no-follow",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "true"
              ]
            },
            "description": "Fetch once and close the stream instead of tailing forever."
          },
          {
            "name": "since",
            "in": "query",
            "schema": {
              "type": "string",
              "example": "1h"
            },
            "description": "Go duration string (\"1h\", \"30m\", \"2h30m\"); implies historical fetch."
          }
        ],
        "responses": {
          "200": {
            "description": "`text/event-stream` of raw log lines (or one JSON object per line with `format=json`).",
            "content": {
              "text/event-stream": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "404": {
            "description": "App not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "First log fetch failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/apps/{name}/health": {
      "get": {
        "tags": [
          "Health"
        ],
        "summary": "Machine state + HTTP metrics (last 1h) for an app",
        "x-audience": "frontend",
        "parameters": [
          {
            "$ref": "#/components/parameters/AppName"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HealthDetail"
                }
              }
            }
          },
          "404": {
            "description": "App not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "Could not list machines",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/apps/{name}/diagnosis": {
      "get": {
        "tags": [
          "Health"
        ],
        "summary": "Why the app is or is not answering, with the evidence",
        "description": "The owner's side of the public failure page, which only ever says\n\"not responding\". Reads the machines' states, runs one read-only\ncommand inside up to three running machines to see what listens on\nthe app's port, and reads the last deployment. Never starts, stops or\nchanges anything. Every read is fresh, under its own per-account\nrate limit (6 a minute), separate from the machine-operation one.\n",
        "x-audience": "agent",
        "parameters": [
          {
            "$ref": "#/components/parameters/AppName"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AppDiagnosis"
                }
              }
            }
          },
          "404": {
            "description": "App not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Diagnosis rate limit reached",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Diagnosis is not available on this server",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/billing/plan": {
      "get": {
        "tags": [
          "Billing"
        ],
        "summary": "Current plan, limits, overage rates, and usage-vs-limits",
        "x-audience": "frontend",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BillingPlan"
                }
              }
            }
          }
        }
      }
    },
    "/billing/addons/checkout": {
      "post": {
        "tags": [
          "Billing"
        ],
        "summary": "Buy an add-on — a performance machine (monthly) or traffic packs (prepaid)",
        "description": "A performance add-on opens a recurring Suby checkout for the add-on's\nown product, one machine per subscription; the webhook grants it when\nthe first charge lands. A traffic pack is the one-time traffic top-up,\nsold by the pack. See docs/addons.md.\n",
        "x-audience": "frontend",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "addon": {
                    "type": "string",
                    "enum": [
                      "performance-1x",
                      "performance-4x",
                      "traffic-100gb"
                    ]
                  },
                  "quantity": {
                    "type": "integer",
                    "minimum": 1,
                    "description": "Traffic packs only"
                  }
                },
                "required": [
                  "addon"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Hosted checkout opened",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "checkout_url": {
                      "type": "string"
                    },
                    "addon": {
                      "type": "string"
                    },
                    "price": {
                      "type": "string"
                    },
                    "gb": {
                      "type": "string"
                    },
                    "price_usd": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "checkout_url"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Unknown add-on (`unknown_addon`), or an invalid quantity",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Performance add-ons are sold on Hobby only. Free is refused with `addon_needs_paid_plan`; Pro and Team, which include performance CPU, with `addon_not_for_plan`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "A checkout for this add-on is already open (`checkout_in_progress`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "Payment gateway error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "The add-on is not on sale on this server (`addon_not_on_sale`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/billing/addons/cancel": {
      "post": {
        "tags": [
          "Billing"
        ],
        "summary": "Cancel a performance add-on at the end of its paid month",
        "description": "Cancels the add-on's own monthly subscription at period end: the slot\nruns to the time paid for, then ends, with no renewal grace. Nothing\nis refunded or cut short, and the plan is untouched (cancelling the\nplan does not cancel add-ons). Name the slot (`/billing/plan`\n`addons.active[].slot`), or the add-on when the account holds exactly\none renewing unit of it. Asking again is a no-op. See docs/addons.md.\n",
        "x-audience": "frontend",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "slot": {
                    "type": "string",
                    "description": "e.g. sub:sub_123"
                  },
                  "addon": {
                    "type": "string",
                    "enum": [
                      "performance-1x",
                      "performance-4x"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Cancelled at period end (or already cancelled, with `note`)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "cancelled": {
                      "type": "boolean"
                    },
                    "slot": {
                      "type": "string"
                    },
                    "addon": {
                      "type": "string"
                    },
                    "ends_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "note": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "cancelled",
                    "slot",
                    "addon",
                    "ends_at"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Nothing named, or an unknown add-on (`unknown_addon`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such active add-on on the account (`addon_not_found`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "A one-time purchase that does not renew (`addon_does_not_renew`), or several renewing units of the add-on and no slot named (`addon_slot_ambiguous`, with `slots`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Cancelled at the gateway but not recorded; retrying is safe",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "Payment gateway error; nothing was cancelled",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Add-ons are not sold on this server (`not_configured`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/billing/usage": {
      "get": {
        "tags": [
          "Billing"
        ],
        "summary": "Current-period usage, broken down per app and totaled per meter",
        "x-audience": "frontend",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BillingUsage"
                }
              }
            }
          },
          "500": {
            "description": "Internal error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/apps/{name}/usage": {
      "get": {
        "tags": [
          "Billing"
        ],
        "summary": "Current-period usage for one app",
        "x-audience": "frontend",
        "parameters": [
          {
            "$ref": "#/components/parameters/AppName"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AppUsage"
                }
              }
            }
          },
          "404": {
            "description": "App not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/billing/alert": {
      "put": {
        "tags": [
          "Billing"
        ],
        "summary": "Set (or replace) the spend-alert threshold",
        "x-audience": "frontend",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "max_cents": {
                    "type": "integer",
                    "minimum": 1,
                    "example": 2000,
                    "description": "e.g. 2000 = $20.00"
                  }
                },
                "required": [
                  "max_cents"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "max_cents": {
                      "type": "integer"
                    },
                    "display": {
                      "type": "string",
                      "example": "$20.00"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "active"
                      ]
                    }
                  },
                  "required": [
                    "max_cents",
                    "display",
                    "status"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Missing/invalid max_cents",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "Billing"
        ],
        "summary": "Get the current spend-alert config",
        "x-audience": "frontend",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "type": "object",
                      "description": "No alert configured.",
                      "properties": {
                        "status": {
                          "type": "string",
                          "enum": [
                            "not_set"
                          ]
                        }
                      },
                      "required": [
                        "status"
                      ]
                    },
                    {
                      "type": "object",
                      "description": "Alert configured.",
                      "properties": {
                        "max_cents": {
                          "type": "integer"
                        },
                        "display": {
                          "type": "string"
                        },
                        "current_cents": {
                          "type": "integer"
                        },
                        "current_display": {
                          "type": "string"
                        },
                        "exceeded": {
                          "type": "boolean"
                        },
                        "status": {
                          "type": "string",
                          "enum": [
                            "active"
                          ]
                        }
                      },
                      "required": [
                        "max_cents",
                        "display",
                        "current_cents",
                        "current_display",
                        "exceeded",
                        "status"
                      ]
                    }
                  ]
                }
              }
            }
          },
          "500": {
            "description": "Internal error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Billing"
        ],
        "summary": "Remove the spend alert",
        "x-audience": "frontend",
        "responses": {
          "200": {
            "description": "Removed",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string",
                      "enum": [
                        "removed"
                      ]
                    }
                  },
                  "required": [
                    "status"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/subscription": {
      "get": {
        "tags": [
          "Subscription"
        ],
        "summary": "Current subscription status",
        "description": "Returns the account's current plan. Legacy card subscriptions may\ninclude additional provider metadata fields; clients must rely only\non the documented fields.\n",
        "x-audience": "frontend",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StoreSubscriptionStatus"
                }
              }
            }
          },
          "400": {
            "description": "User email not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/subscription/checkout": {
      "post": {
        "tags": [
          "Subscription"
        ],
        "summary": "Create a checkout URL for upgrading (hosted payment page — card + crypto)",
        "x-audience": "frontend",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "plan": {
                    "type": "string",
                    "enum": [
                      "hobby",
                      "pro",
                      "team"
                    ]
                  }
                },
                "required": [
                  "plan"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "checkout_url": {
                      "type": "string"
                    },
                    "plan": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "checkout_url",
                    "plan"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Missing/invalid plan, or user email not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "The account can't start this checkout yet:\n- `crypto_subscription_active` — an active crypto subscription must\n  be cancelled first (dual-rail guard).\n- `already_on_plan` — already subscribed to the requested plan.\n- `cancel_first` — on a different recurring plan; cancel it first\n  (the paid period is kept), then pick the new plan.\n",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "code": {
                      "type": "string",
                      "enum": [
                        "crypto_subscription_active",
                        "already_on_plan",
                        "cancel_first"
                      ]
                    }
                  },
                  "required": [
                    "error",
                    "code"
                  ]
                }
              }
            }
          },
          "502": {
            "description": "Payment gateway error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Card checkout not available — the payment gateway isn't configured\non this server and no other card-payment provider accepts new\nsignups.\n",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "code": {
                      "type": "string",
                      "enum": [
                        "card_coming_soon"
                      ]
                    }
                  },
                  "required": [
                    "error",
                    "code"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/subscription/cancel": {
      "post": {
        "tags": [
          "Subscription"
        ],
        "summary": "Cancel the active subscription (whichever rail it's on)",
        "x-audience": "frontend",
        "responses": {
          "200": {
            "description": "Cancelled",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "type": "object",
                      "properties": {
                        "rail": {
                          "type": "string",
                          "enum": [
                            "card"
                          ]
                        },
                        "cancelled": {
                          "type": "boolean"
                        },
                        "raw": {
                          "type": "object",
                          "description": "Raw gateway CancelResponse passthrough."
                        },
                        "addons_renewing": {
                          "type": "array",
                          "description": "Performance add-ons still renewing: separate subscriptions the plan's cancel leaves alone. Absent when there are none.",
                          "items": {
                            "type": "object",
                            "properties": {
                              "slot": {
                                "type": "string"
                              },
                              "addon": {
                                "type": "string"
                              },
                              "name": {
                                "type": "string"
                              },
                              "price_text": {
                                "type": "string"
                              },
                              "renews_at": {
                                "type": "string",
                                "format": "date-time"
                              }
                            }
                          }
                        },
                        "addons_note": {
                          "type": "string",
                          "description": "The sentence to show about addons_renewing, naming how to cancel them."
                        }
                      },
                      "required": [
                        "rail",
                        "cancelled",
                        "raw"
                      ]
                    },
                    {
                      "type": "object",
                      "properties": {
                        "rail": {
                          "type": "string",
                          "enum": [
                            "crypto"
                          ]
                        },
                        "cancelled": {
                          "type": "boolean"
                        },
                        "hint": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "rail",
                        "cancelled",
                        "hint"
                      ]
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "User email not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No active subscription",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "The payment gateway (or the crypto subscription service) failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/subscription/invoices": {
      "get": {
        "tags": [
          "Subscription"
        ],
        "summary": "Payments this account was charged, newest first",
        "description": "What the account was actually charged through the payment gateway,\none row per payment with an amount. These are payments, not tax\ninvoices: the gateway is merchant of record and issues those. An\naccount with no recurring subscription, or a deployment without the\ngateway, answers an empty list.\n",
        "x-audience": "frontend",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "invoices": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Invoice"
                      }
                    }
                  },
                  "required": [
                    "invoices"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          },
          "502": {
            "$ref": "#/components/responses/UpstreamError"
          }
        }
      }
    },
    "/subscription/topup/preview": {
      "get": {
        "tags": [
          "Topup"
        ],
        "summary": "Preview the top-up rate for a plan",
        "x-audience": "frontend",
        "parameters": [
          {
            "name": "plan",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "hobby",
                "pro",
                "team"
              ]
            },
            "description": "Defaults to the user's current paid tier, or \"hobby\" if free."
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TopupPreview"
                }
              }
            }
          },
          "400": {
            "description": "Invalid plan",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "USDC payments are paused (the payment settings disagree)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaymentsPaused"
                }
              }
            }
          }
        }
      }
    },
    "/subscription/topup/recent": {
      "get": {
        "tags": [
          "Topup"
        ],
        "summary": "Recent top-up credits (newest first)",
        "x-audience": "frontend",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 20,
              "maximum": 100
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "credits": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/TopupCredit"
                      }
                    }
                  },
                  "required": [
                    "credits"
                  ]
                }
              }
            }
          },
          "500": {
            "description": "Internal error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/subscription/topup/x402": {
      "get": {
        "tags": [
          "Topup"
        ],
        "summary": "x402 payment challenge for a USDC top-up",
        "description": "Two-step handshake:\n  1. `GET` with no payment header → `402` with an `accepts` array\n     describing what to pay, where, and what it buys. The fallback\n     settlement path (manual on-chain transfer, matched by sender\n     address) requires a bound wallet first; watch\n     `GET /subscription/topup/recent` for the credit to land.\n  2. `GET` again with a `PAYMENT-SIGNATURE` (or legacy `X-PAYMENT`)\n     header carrying a base64-JSON signed payment → verified +\n     settled instantly through the configured facilitator, `200`\n     with the credited days. The settlement response also carries a\n     `PAYMENT-RESPONSE` header (base64 JSON).\n",
        "x-audience": "frontend",
        "parameters": [
          {
            "name": "amount_usd",
            "in": "query",
            "required": true,
            "schema": {
              "type": "number",
              "format": "float",
              "example": 5
            }
          },
          {
            "name": "PAYMENT-SIGNATURE",
            "in": "header",
            "schema": {
              "type": "string"
            },
            "description": "Base64-encoded JSON signed payment payload (step 2). `X-PAYMENT` is accepted as a legacy alias."
          }
        ],
        "responses": {
          "200": {
            "description": "Payment verified and settled — days credited",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "type": "object",
                      "properties": {
                        "status": {
                          "type": "string",
                          "enum": [
                            "credited",
                            "debt_settled"
                          ],
                          "description": "debt_settled when the whole payment paid off lapse-grace debt and bought no plan time."
                        },
                        "days_granted": {
                          "type": "string",
                          "example": "3.5"
                        },
                        "plan": {
                          "type": "string"
                        },
                        "tx_hash": {
                          "type": "string"
                        },
                        "expires_at": {
                          "type": "string",
                          "format": "date-time",
                          "description": "Absent when the payment bought no plan time."
                        },
                        "debt_settled_days": {
                          "type": "string",
                          "description": "Days of lapse-grace debt the payment paid off, when any."
                        },
                        "days_applied": {
                          "type": "string",
                          "description": "Days that went to the plan, when part of the payment paid off debt."
                        },
                        "debt_remaining_days": {
                          "type": "string",
                          "description": "Debt still owed after this payment, when any."
                        }
                      },
                      "required": [
                        "status",
                        "days_granted",
                        "plan",
                        "tx_hash"
                      ]
                    },
                    {
                      "type": "object",
                      "description": "Idempotent replay of an already-processed transaction.",
                      "properties": {
                        "status": {
                          "type": "string",
                          "enum": [
                            "already_credited"
                          ]
                        },
                        "tx_hash": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "status",
                        "tx_hash"
                      ]
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Missing/invalid amount_usd, or below the one-hour minimum / above the 10000 USDC max",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "Payment required (no valid payment attached yet), or the attached payment was invalid/failed to settle",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "x402Version": {
                      "type": "integer",
                      "enum": [
                        2
                      ]
                    },
                    "error": {
                      "type": "string"
                    },
                    "accepts": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/X402Requirements"
                      }
                    },
                    "plan": {
                      "type": "string"
                    },
                    "days_estimate": {
                      "type": "string"
                    },
                    "bound_wallets": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "network_name": {
                      "type": "string",
                      "example": "Base Sepolia (testnet)"
                    },
                    "testnet": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "500": {
            "description": "Internal error. Before the payment is settled (user lookup, the quote) it is\nINTERNAL_ERROR. After it settled, the account could not be credited: the code is\nPAYMENT_NOT_CREDITED, not retryable, and the body carries `tx_hash` — do not pay\nagain; check `GET /subscription/topup/recent`.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaymentNotCreditedError"
                }
              }
            }
          },
          "502": {
            "description": "The facilitator could not verify the payment (UPSTREAM_ERROR: nothing was taken,\nsend the same request again), or settlement did not answer or answered something\nunusable (OUTCOME_UNKNOWN: the payment may have gone through — check\n`GET /subscription/topup/recent` before paying again).\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/billing/fit": {
      "get": {
        "tags": [
          "Billing"
        ],
        "summary": "Whether this account's resources fit another plan, and what is over",
        "description": "Asked before a downgrade; `resources` is everything that uses the pool, with what removing each frees.",
        "x-audience": "frontend",
        "parameters": [
          {
            "name": "plan",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "free",
                "hobby",
                "pro",
                "team"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "plan": {
                      "type": "string"
                    },
                    "fits": {
                      "type": "boolean"
                    },
                    "over_text": {
                      "type": "string"
                    },
                    "over": {
                      "type": "object",
                      "properties": {
                        "memory_mb": {
                          "type": "integer"
                        },
                        "volume_gb": {
                          "type": "integer"
                        },
                        "apps": {
                          "type": "integer"
                        },
                        "sites": {
                          "type": "integer"
                        },
                        "domains": {
                          "type": "integer"
                        }
                      }
                    },
                    "resources": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "kind": {
                            "type": "string",
                            "enum": [
                              "app",
                              "site",
                              "agent"
                            ]
                          },
                          "name": {
                            "type": "string"
                          },
                          "memory_mb": {
                            "type": "integer"
                          },
                          "volume_gb": {
                            "type": "integer"
                          },
                          "frees": {
                            "type": "string"
                          },
                          "size_bytes": {
                            "type": "integer"
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    }
                  },
                  "required": [
                    "plan",
                    "fits",
                    "over_text",
                    "over",
                    "resources"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/billing/pricing": {
      "get": {
        "tags": [
          "Billing"
        ],
        "summary": "The account's pricing version, and a preview of switching to the one on sale",
        "description": "`current` is the account's plan in its own pricing version. While\nanother version is on sale (pricing v2) and the account is not on\nit, `offer` is the same tier in that version, `blockers` what must\nchange before the switch, and `one_way` says the switch cannot be\nundone. A free account with a balance saved toward a paid plan, or a\nlapsed plan it can renew, also gets `pending`: that plan in both\nversions (it does not block). Reading changes nothing.\n",
        "x-audience": "frontend",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "version": {
                      "type": "string",
                      "description": "The account's pricing version"
                    },
                    "on_sale": {
                      "type": "string",
                      "description": "The version new accounts get"
                    },
                    "current": {
                      "$ref": "#/components/schemas/PricingPlanCard"
                    },
                    "can_switch": {
                      "type": "boolean"
                    },
                    "reason": {
                      "type": "string",
                      "description": "Why there is nothing to switch to"
                    },
                    "offer": {
                      "$ref": "#/components/schemas/PricingPlanCard"
                    },
                    "blockers": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "one_way": {
                      "type": "string"
                    },
                    "pending": {
                      "type": "object",
                      "properties": {
                        "reason": {
                          "type": "string"
                        },
                        "current": {
                          "$ref": "#/components/schemas/PricingPlanCard"
                        },
                        "offer": {
                          "$ref": "#/components/schemas/PricingPlanCard"
                        }
                      }
                    }
                  },
                  "required": [
                    "version",
                    "on_sale",
                    "current",
                    "can_switch"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "502": {
            "$ref": "#/components/responses/UpstreamError"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      },
      "post": {
        "tags": [
          "Billing"
        ],
        "summary": "Switch the account to the pricing version on sale (one way)",
        "description": "Moves the account to the version on sale if it already fits that\nversion's plan. Tier, price and subscription are kept; nothing is\nstopped or deleted. Read `GET /billing/pricing` first and confirm\nwith the owner: the switch cannot be undone. Sent again after it\nlanded, it answers 200 with `switched: false`.\n",
        "x-audience": "frontend",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "version": {
                    "type": "string",
                    "enum": [
                      "1",
                      "2"
                    ],
                    "description": "The version to move to. Only \"2\" switches, and only an\naccount on \"1\". The account's own version changes\nnothing (200, `switched: false`); \"1\" from an account on\n\"2\" is refused (409 PRICING_SWITCH_NOT_ALLOWED).\n"
                  }
                },
                "required": [
                  "version"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Switched, or already on that version",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "version": {
                      "type": "string"
                    },
                    "switched": {
                      "type": "boolean"
                    },
                    "switched_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "plan": {
                      "$ref": "#/components/schemas/PricingPlanCard"
                    }
                  },
                  "required": [
                    "version",
                    "switched",
                    "plan"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "409": {
            "description": "Pool write contention (POOL_LOCKED, send it again shortly), or the\nswitch is refused: PRICING_NOT_ON_SALE (pricing v2 is off, for\neither version), PRICING_SWITCH_NOT_ALLOWED (\"1\" from an account\non \"2\": the switch is one way), PRICING_SWITCH_DOES_NOT_FIT (with\n`blockers`) or PRICING_VERSION_CHANGED (the plan changed while\nswitching; nothing was written)\n",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/PoolLockedError"
                    },
                    {
                      "$ref": "#/components/schemas/Error"
                    }
                  ]
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          },
          "502": {
            "$ref": "#/components/responses/UpstreamError"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/billing/egress/topup/x402": {
      "get": {
        "tags": [
          "Topup"
        ],
        "summary": "x402 payment challenge for a traffic top-up (GB)",
        "description": "Same handshake as `/subscription/topup/x402`, but the payment buys\ntraffic: `GET` with no payment header answers 402 with what to pay;\n`GET` again with a signed payment header settles it and credits the\nGB. A raw transfer to the deposit address buys plan days, not\ntraffic. Mounted while the deposit rail is configured.\n",
        "x-audience": "cli",
        "parameters": [
          {
            "name": "gb",
            "in": "query",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "PAYMENT-SIGNATURE",
            "in": "header",
            "schema": {
              "type": "string"
            },
            "description": "Base64-encoded JSON signed payment payload (step 2). `X-PAYMENT` is accepted as a legacy alias."
          }
        ],
        "responses": {
          "200": {
            "description": "Payment settled; traffic credited",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string",
                      "enum": [
                        "credited",
                        "already_credited"
                      ]
                    },
                    "gb": {
                      "type": "integer"
                    },
                    "tx_hash": {
                      "type": "string"
                    },
                    "balance_gb": {
                      "type": "string",
                      "example": "100.00"
                    }
                  },
                  "required": [
                    "status",
                    "gb",
                    "tx_hash"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "description": "Payment required, or the attached payment was invalid or failed to settle",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "x402Version": {
                      "type": "integer",
                      "enum": [
                        2
                      ]
                    },
                    "error": {
                      "type": "string"
                    },
                    "accepts": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/X402Requirements"
                      }
                    },
                    "gb": {
                      "type": "integer"
                    },
                    "price_usd": {
                      "type": "string"
                    },
                    "note": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "500": {
            "description": "The payment settled but the traffic could not be credited (PAYMENT_NOT_CREDITED, not\nretryable; the body carries `tx_hash`). Do not pay again: check the traffic balance\n(`GET /billing/usage`).\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaymentNotCreditedError"
                }
              }
            }
          },
          "502": {
            "description": "The facilitator could not verify the payment (UPSTREAM_ERROR: nothing was taken,\nsend the same request again), or settlement did not answer or answered something\nunusable (OUTCOME_UNKNOWN: the payment may have gone through — check the traffic\nbalance before paying again).\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Traffic top-ups are not enabled on this deployment (NOT_ENABLED)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/billing/egress/topup/checkout": {
      "post": {
        "tags": [
          "Topup"
        ],
        "summary": "Hosted card checkout for a traffic top-up",
        "x-audience": "frontend",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "gb": {
                    "type": "integer",
                    "minimum": 1
                  }
                },
                "required": [
                  "gb"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Checkout created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "checkout_url": {
                      "type": "string",
                      "format": "uri"
                    },
                    "gb": {
                      "type": "string"
                    },
                    "price_usd": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "checkout_url",
                    "gb",
                    "price_usd"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "502": {
            "$ref": "#/components/responses/UpstreamError"
          },
          "503": {
            "description": "Card checkout is not enabled on this deployment (NOT_ENABLED); top up with USDC instead",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/billing/debt/checkout": {
      "post": {
        "tags": [
          "Subscription"
        ],
        "summary": "Hosted card checkout to settle days used past the paid period",
        "x-audience": "frontend",
        "responses": {
          "200": {
            "description": "`owed: false` when nothing is owed; otherwise a checkout",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "owed": {
                      "type": "boolean"
                    },
                    "checkout_url": {
                      "type": "string",
                      "format": "uri"
                    },
                    "days": {
                      "type": "string"
                    },
                    "price_usd": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "owed"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "502": {
            "$ref": "#/components/responses/UpstreamError"
          },
          "503": {
            "description": "Card checkout is not enabled on this deployment (NOT_ENABLED); settle with USDC instead",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/subscription/change-plan": {
      "post": {
        "tags": [
          "Subscription"
        ],
        "summary": "Switch an active recurring subscription in place",
        "description": "An upgrade takes effect now and charges the prorated difference for\nthe rest of the paid period; a downgrade waits for the period's end.\nWhen the charge needs the payer (card verification), `effective` is\n`pending` and `checkout_url` is where they finish. Plan ids are API\nidentifiers: `team` is shown as \"Studio\".\n",
        "x-audience": "frontend",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "plan": {
                    "type": "string",
                    "enum": [
                      "hobby",
                      "pro",
                      "team"
                    ]
                  }
                },
                "required": [
                  "plan"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Changed now, scheduled for the period end, or pending the payer",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "changed": {
                      "type": "boolean"
                    },
                    "plan": {
                      "type": "string"
                    },
                    "effective": {
                      "type": "string",
                      "enum": [
                        "now",
                        "period_end",
                        "pending"
                      ]
                    },
                    "effective_at": {
                      "type": "string"
                    },
                    "checkout_url": {
                      "type": "string",
                      "format": "uri"
                    }
                  },
                  "required": [
                    "changed",
                    "plan",
                    "effective"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "description": "The upgrade charge was declined (payment_declined)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "no_subscription, already_on_plan, checkout_in_progress, billing_state_mismatch or change_reconciliation_pending",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "The gateway changed the plan but the account's record has not caught up\n(OUTCOME_UNKNOWN, not retryable): read GET /subscription shortly\nrather than sending the change again. Other internal errors are INTERNAL_ERROR.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "The subscription could not be verified with the gateway before anything changed\n(UPSTREAM_ERROR), or the change call failed or answered something unreadable\n(OUTCOME_UNKNOWN — it may have taken effect: check the subscription before retrying).\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Plan changes are not available on this deployment (not_configured), or coordination is briefly unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/subscription/change-plan/preview": {
      "get": {
        "tags": [
          "Subscription"
        ],
        "summary": "What a plan change would do and cost, before anything is charged",
        "x-audience": "frontend",
        "parameters": [
          {
            "name": "plan",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "hobby",
                "pro",
                "team"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "plan": {
                      "type": "string"
                    },
                    "direction": {
                      "type": "string",
                      "enum": [
                        "upgrade",
                        "downgrade"
                      ]
                    },
                    "effective": {
                      "type": "string",
                      "enum": [
                        "now",
                        "period_end"
                      ]
                    },
                    "effective_at": {
                      "type": "string"
                    },
                    "estimated_charge": {
                      "type": "string",
                      "description": "Upgrades only; an estimate of the prorated charge."
                    },
                    "renews_at": {
                      "type": "string"
                    },
                    "renews_price": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "plan",
                    "direction",
                    "effective"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "409": {
            "description": "no_subscription, already_on_plan or scheduled_change_active",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "$ref": "#/components/responses/UpstreamError"
          },
          "503": {
            "description": "Plan changes are not available on this deployment (not_configured)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/subscription/change-plan/cancel": {
      "post": {
        "tags": [
          "Subscription"
        ],
        "summary": "Call off a scheduled plan change",
        "x-audience": "frontend",
        "responses": {
          "200": {
            "description": "Called off; the current plan renews",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "cancelled": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "plan": {
                      "type": "string"
                    },
                    "renews_at": {
                      "type": "string"
                    },
                    "renews_price": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "cancelled",
                    "plan"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "409": {
            "description": "no_subscription, no_scheduled_change, change_not_cancelable or checkout_in_progress",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "The gateway called the change off but the account's record has not caught up (OUTCOME_UNKNOWN, not retryable); read GET /subscription shortly",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "$ref": "#/components/responses/UpstreamError"
          },
          "503": {
            "description": "Plan changes are not available on this deployment (not_configured)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "USDC payments are paused (the payment settings disagree)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaymentsPaused"
                }
              }
            }
          }
        }
      }
    },
    "/auth/wallet/bind": {
      "post": {
        "tags": [
          "Wallet"
        ],
        "summary": "Bind a wallet address to the current account",
        "description": "At most one binding per user — binding a new address drops any\nprior binding for a different address. Idempotent for the same\naddress. Verification is immediate (`status: \"verified\"`); the\nactual proof of ownership happens implicitly the first time the\nrail is used.\n",
        "x-audience": "frontend",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "address": {
                    "type": "string",
                    "example": "0xabc...123"
                  }
                },
                "required": [
                  "address"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Bound",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string",
                      "enum": [
                        "verified"
                      ]
                    },
                    "address": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "status",
                    "address"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Missing/invalid address",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Address already bound to a different account",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "USDC payments are paused (the payment settings disagree)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaymentsPaused"
                }
              }
            }
          }
        }
      }
    },
    "/auth/wallet": {
      "get": {
        "tags": [
          "Wallet"
        ],
        "summary": "List wallet bindings for the current user",
        "x-audience": "frontend",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "deposit_address": {
                      "type": "string"
                    },
                    "bindings": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/WalletBinding"
                      }
                    }
                  },
                  "required": [
                    "deposit_address",
                    "bindings"
                  ]
                }
              }
            }
          },
          "500": {
            "description": "Internal error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "USDC payments are paused (the payment settings disagree)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaymentsPaused"
                }
              }
            }
          }
        }
      }
    },
    "/auth/wallet/{address}": {
      "delete": {
        "tags": [
          "Wallet"
        ],
        "summary": "Unbind a wallet address",
        "x-audience": "frontend",
        "parameters": [
          {
            "name": "address",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Unbound",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "unbound": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "unbound"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Invalid address",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Binding not found (or owned by a different account — same 404 to avoid leaking ownership)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/apps/{name}/runner-deploy": {
      "post": {
        "tags": [
          "Deployments"
        ],
        "summary": "Deploy the shared generic-VM runner image",
        "description": "Boots the platform's shared shell-runner image. `image`, `cmd`,\n`init`, and `rootfs` in the request body are ignored/overridden —\nonly `port`, `env`, `secrets`, `autostop`, and `min_machines` are\nhonored. Response shape is identical to a completed image deploy.\n",
        "x-audience": "agent",
        "parameters": [
          {
            "$ref": "#/components/parameters/AppName"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RunnerDeployRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Deployed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeployResult"
                }
              }
            }
          },
          "400": {
            "description": "Invalid body, or the app has autoscale_max \u003e 1 (runner apps can't autoscale)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "Account over its plan pool",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OverPoolError"
                }
              }
            }
          },
          "403": {
            "description": "Would add always-on cost the plan does not cover (ALWAYS_ON_COST_LIMIT / ALWAYS_ON_PERFORMANCE); nothing was deployed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TierViolationError"
                }
              }
            }
          },
          "404": {
            "description": "App not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "A deploy is already in progress on this app",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "code": {
                      "type": "string",
                      "enum": [
                        "DEPLOY_IN_PROGRESS"
                      ]
                    }
                  },
                  "required": [
                    "error",
                    "code"
                  ]
                }
              }
            }
          },
          "502": {
            "description": "Upstream machine create/update/start failure",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Runner image not configured on this server, or the always-on check could not read what it needs for a deploy that could add always-on cost (LIMITS_UNAVAILABLE; nothing changed, retry)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/apps/{name}/stop": {
      "post": {
        "tags": [
          "Machines (Agent)"
        ],
        "summary": "Stop all running machines",
        "x-audience": "agent",
        "parameters": [
          {
            "$ref": "#/components/parameters/AppName"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "stopped": {
                      "type": "integer"
                    },
                    "total": {
                      "type": "integer"
                    }
                  },
                  "required": [
                    "stopped",
                    "total"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "App not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "Could not list machines",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/apps/{name}/start": {
      "post": {
        "tags": [
          "Machines (Agent)"
        ],
        "summary": "Start all stopped/suspended machines",
        "x-audience": "agent",
        "parameters": [
          {
            "$ref": "#/components/parameters/AppName"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "started": {
                      "type": "integer"
                    },
                    "total": {
                      "type": "integer"
                    }
                  },
                  "required": [
                    "started",
                    "total"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "App not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "Could not list machines",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/apps/{name}/restart": {
      "post": {
        "tags": [
          "Machines (Agent)"
        ],
        "summary": "Restart every machine (re-injects current env/secrets)",
        "x-audience": "agent",
        "parameters": [
          {
            "$ref": "#/components/parameters/AppName"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "restarted": {
                      "type": "integer"
                    },
                    "total": {
                      "type": "integer"
                    }
                  },
                  "required": [
                    "restarted",
                    "total"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "App not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "Could not list machines",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/apps/{name}/machines": {
      "get": {
        "tags": [
          "Machines (Agent)"
        ],
        "summary": "List machines",
        "x-audience": "agent",
        "parameters": [
          {
            "$ref": "#/components/parameters/AppName"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "machines": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Machine"
                      }
                    },
                    "count": {
                      "type": "integer"
                    }
                  },
                  "required": [
                    "machines",
                    "count"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "App not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "Could not list machines",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/apps/{name}/machines/{machineId}": {
      "delete": {
        "tags": [
          "Machines (Agent)"
        ],
        "summary": "Destroy a single machine",
        "x-audience": "agent",
        "parameters": [
          {
            "$ref": "#/components/parameters/AppName"
          },
          {
            "name": "machineId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Destroyed",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "destroyed": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "destroyed"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "App not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "Destroy failed upstream",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/apps/{name}/uploads/{uploadID}": {
      "get": {
        "tags": [
          "Exec (Agent)"
        ],
        "summary": "Read a resumable runner-upload cursor",
        "description": "Internal CLI transport. Returns the next chunk index that has not been acknowledged.",
        "x-audience": "agent",
        "parameters": [
          {
            "$ref": "#/components/parameters/AppName"
          },
          {
            "name": "uploadID",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{16}$"
            }
          },
          {
            "name": "sha256",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{64}$"
            }
          },
          {
            "name": "machine",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Cursor read",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "next": {
                      "type": "integer",
                      "minimum": 0
                    }
                  },
                  "required": [
                    "next"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Invalid upload identity",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "App or requested running machine not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Remote cursor identity mismatch",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "Invalid remote cursor",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/apps/{name}/uploads/{uploadID}/chunks/{index}": {
      "put": {
        "tags": [
          "Exec (Agent)"
        ],
        "summary": "Commit one verified runner-upload chunk",
        "description": "Internal CLI transport. The backend bounds the raw request at 8 MiB,\nverifies its SHA-256, transfers it to the selected runner with SFTP,\nverifies it again remotely, then atomically advances the cursor.\n",
        "x-audience": "agent",
        "parameters": [
          {
            "$ref": "#/components/parameters/AppName"
          },
          {
            "name": "uploadID",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{16}$"
            }
          },
          {
            "name": "index",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 0,
              "maximum": 99999999
            }
          },
          {
            "name": "machine",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Ifhost-Upload-SHA256",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{64}$"
            }
          },
          {
            "name": "X-Ifhost-Chunk-SHA256",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{64}$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/octet-stream": {
              "schema": {
                "type": "string",
                "format": "binary",
                "maxLength": 8388608
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Chunk acknowledged or already committed",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "next": {
                      "type": "integer",
                      "minimum": 1
                    },
                    "skipped": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "next",
                    "skipped"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Invalid identity, empty chunk, or checksum mismatch",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "App or requested running machine not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Remote cursor mismatch",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Chunk exceeds 8 MiB"
          },
          "502": {
            "description": "SFTP or remote commit failure",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/apps/{name}/exec": {
      "post": {
        "tags": [
          "Exec (Agent)"
        ],
        "summary": "Run a one-shot command inside a running machine",
        "description": "Argv is passed through unchanged (no implicit shell wrapping) — use\n`[\"sh\", \"-c\", \"...\"]` explicitly for shell semantics. Long-running\ncommands should be launched with `nohup ... \u003e /tmp/log 2\u003e\u00261 \u0026`,\nsince a single exec call has an upstream deadline (~10 min); a 504\nresponse includes the fire-and-poll guidance.\n",
        "x-audience": "agent",
        "parameters": [
          {
            "$ref": "#/components/parameters/AppName"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "cmd": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "example": [
                      "ls",
                      "/data"
                    ]
                  },
                  "machine": {
                    "type": "string",
                    "description": "Target a specific machine ID (default — first running machine)."
                  }
                },
                "required": [
                  "cmd"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExecResult"
                }
              }
            }
          },
          "400": {
            "description": "Missing cmd",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "App not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "No running machines — start the app first",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "502": {
            "description": "No machines found (UPSTREAM_ERROR, retryable); the output was too large to return\n(EXEC_OUTPUT_TOO_LARGE: the command ran; redirect its output to a file and pull it);\nor the provider's answer was lost (OUTCOME_UNKNOWN: the command may have run).\nNeither of the last two says retryable: sending the same command again runs it again.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "504": {
            "description": "The command ran out of time and may still be running (EXEC_TIMEOUT, not retryable); the message gives the fire-and-poll pattern",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/apps/{name}/console/start": {
      "post": {
        "tags": [
          "Console (Agent)"
        ],
        "summary": "Start a tmux console session",
        "description": "tmux is auto-installed on first use (apt or apk) if missing. If\nneither `cmd` nor `command` is supplied, the session starts bash\n(falling back to sh).\n",
        "x-audience": "agent",
        "parameters": [
          {
            "$ref": "#/components/parameters/AppName"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "cmd": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Argv form."
                  },
                  "command": {
                    "type": "string",
                    "description": "Shell-string form (used if cmd is empty)."
                  },
                  "machine": {
                    "type": "string",
                    "description": "Target machine ID (default — first running machine)."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Started",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "session_id": {
                      "type": "string",
                      "example": "ifhost-01hz3k..."
                    },
                    "machine_id": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "session_id",
                    "machine_id"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Invalid request body",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "App not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "No running machines, or tmux missing and auto-install failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "Upstream exec failure (checking/installing tmux, or starting the session)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/apps/{name}/console/input": {
      "post": {
        "tags": [
          "Console (Agent)"
        ],
        "summary": "Send literal text and/or tmux key names to a session",
        "x-audience": "agent",
        "parameters": [
          {
            "$ref": "#/components/parameters/AppName"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "session_id": {
                    "type": "string"
                  },
                  "machine_id": {
                    "type": "string",
                    "description": "Defaults to the first running machine."
                  },
                  "input": {
                    "type": "string"
                  },
                  "keys": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "example": [
                      "Enter"
                    ],
                    "description": "tmux key names (Enter, C-c, C-d, Up, Down, Left, Right, ...)."
                  }
                },
                "required": [
                  "session_id"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "session_id": {
                      "type": "string"
                    },
                    "sent": {
                      "type": "integer",
                      "description": "Byte length of `input` sent."
                    },
                    "keys": {
                      "type": "integer",
                      "description": "Count of key names sent."
                    }
                  },
                  "required": [
                    "session_id",
                    "sent",
                    "keys"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Invalid session_id",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "App or console session not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "No running machines",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "Upstream exec failure",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/apps/{name}/console/output": {
      "get": {
        "tags": [
          "Console (Agent)"
        ],
        "summary": "Capture a console session's current pane output",
        "x-audience": "agent",
        "parameters": [
          {
            "$ref": "#/components/parameters/AppName"
          },
          {
            "name": "session_id",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "machine_id",
            "in": "query",
            "schema": {
              "type": "string",
              "description": "Defaults to the first running machine."
            }
          },
          {
            "name": "lines",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 200,
              "maximum": 1000
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "session_id": {
                      "type": "string"
                    },
                    "output": {
                      "type": "string"
                    },
                    "lines": {
                      "type": "integer"
                    }
                  },
                  "required": [
                    "session_id",
                    "output",
                    "lines"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Invalid session_id",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "App or console session not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "No running machines",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "Upstream exec failure",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/apps/{name}/console/end": {
      "post": {
        "tags": [
          "Console (Agent)"
        ],
        "summary": "Kill a tmux console session",
        "description": "Sessions are never auto-killed — they live until this is called or\nthe machine is destroyed.\n",
        "x-audience": "agent",
        "parameters": [
          {
            "$ref": "#/components/parameters/AppName"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "session_id": {
                    "type": "string"
                  },
                  "machine": {
                    "type": "string",
                    "description": "Defaults to the first running machine."
                  }
                },
                "required": [
                  "session_id"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Ended",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ended": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "ended"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Invalid session_id",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "App or console session not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "No running machines",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "Upstream exec failure",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/apps/{name}/pull": {
      "post": {
        "tags": [
          "Machines (Agent)"
        ],
        "summary": "Stage a backup of a path on a machine for download (metered traffic)",
        "x-audience": "agent",
        "parameters": [
          {
            "$ref": "#/components/parameters/AppName"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "path": {
                    "type": "string",
                    "description": "Absolute path inside the machine, e.g. /data."
                  },
                  "machine": {
                    "type": "string",
                    "description": "Machine ID; defaults to a running machine."
                  }
                },
                "required": [
                  "path"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Staged; download with GET and the token",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "path": {
                      "type": "string"
                    },
                    "machine": {
                      "type": "string"
                    },
                    "size_bytes": {
                      "type": "integer"
                    },
                    "sha256": {
                      "type": "string"
                    },
                    "token": {
                      "type": "string",
                      "description": "One-time download token."
                    },
                    "expires_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "metered": {
                      "type": "boolean"
                    },
                    "warning": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  },
                  "required": [
                    "path",
                    "machine",
                    "size_bytes",
                    "sha256",
                    "token",
                    "expires_at",
                    "metered"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "No running machine, or the archive could not be made",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "502": {
            "$ref": "#/components/responses/UpstreamError"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          },
          "504": {
            "description": "Archiving ran out of time (EXEC_TIMEOUT, not retryable: the same directory runs out of time again). Back up a smaller directory.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "Machines (Agent)"
        ],
        "summary": "Download a staged backup",
        "x-audience": "agent",
        "parameters": [
          {
            "$ref": "#/components/parameters/AppName"
          },
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The archive",
            "headers": {
              "X-Ifhost-Archive-SHA256": {
                "schema": {
                  "type": "string"
                },
                "description": "Digest to verify the download against."
              },
              "Content-Disposition": {
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/gzip": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "description": "Larger than the remaining traffic allowance and credit",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "502": {
            "$ref": "#/components/responses/UpstreamError"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/apps/{name}/uploads/preflight": {
      "post": {
        "tags": [
          "Machines (Agent)"
        ],
        "summary": "Check a runner upload against the target machine before sending it",
        "description": "Reports files that would overwrite declared state, the space available, and the state paths found.",
        "x-audience": "agent",
        "parameters": [
          {
            "$ref": "#/components/parameters/AppName"
          },
          {
            "$ref": "#/components/parameters/UploadMachine"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "files": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "target": {
                    "type": "string",
                    "description": "Destination directory, e.g. /app."
                  },
                  "state_paths": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  }
                },
                "required": [
                  "files",
                  "target"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "conflicts": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "machine": {
                      "type": "string"
                    },
                    "available_bytes": {
                      "type": "integer"
                    },
                    "state_paths": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "runtime_scan_complete": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "conflicts",
                    "machine",
                    "available_bytes",
                    "state_paths",
                    "runtime_scan_complete"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "502": {
            "$ref": "#/components/responses/UpstreamError"
          }
        }
      }
    },
    "/apps/{name}/uploads/select": {
      "post": {
        "tags": [
          "Machines (Agent)"
        ],
        "summary": "Apply the project's ignore rules to a folder selection (the dashboard's folder upload)",
        "x-audience": "frontend",
        "parameters": [
          {
            "$ref": "#/components/parameters/AppName"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "files": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Relative paths in the selected folder."
                  },
                  "documents": {
                    "type": "object",
                    "additionalProperties": {
                      "type": "string"
                    },
                    "description": "Ignore files found in the folder (path to contents)."
                  }
                },
                "required": [
                  "files"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The files to upload",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "files": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  },
                  "required": [
                    "files"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "413": {
            "description": "Selection larger than the route accepts (plain text)",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/apps/{name}/uploads/{uploadID}/complete": {
      "post": {
        "tags": [
          "Machines (Agent)"
        ],
        "summary": "Move a finished upload into place on the machine (one verified transaction)",
        "description": "Never retry an uncertain answer automatically: a 502\n`OUTCOME_UNKNOWN` means the replacement may have happened. Inspect\nthe target first. A 200 means the command ran; check `exit_code`.\n",
        "x-audience": "agent",
        "parameters": [
          {
            "$ref": "#/components/parameters/AppName"
          },
          {
            "name": "uploadID",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/UploadMachine"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "request_id": {
                    "type": "string"
                  },
                  "kind": {
                    "type": "string"
                  },
                  "target": {
                    "type": "string"
                  },
                  "sha256": {
                    "type": "string"
                  },
                  "empty": {
                    "type": "boolean"
                  },
                  "replace": {
                    "type": "boolean"
                  },
                  "state_paths": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  }
                },
                "required": [
                  "request_id",
                  "kind",
                  "target",
                  "sha256"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The completion command ran",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ExecResult"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "output_truncated": {
                          "type": "boolean"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "502": {
            "description": "Outcome unknown (OUTCOME_UNKNOWN) — inspect the target before retrying",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/agents": {
      "get": {
        "tags": [
          "Assistants"
        ],
        "summary": "The assistant catalog, with what each needs to start",
        "x-audience": "frontend",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "agents": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/AgentCatalogEntry"
                      }
                    }
                  },
                  "required": [
                    "agents"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      },
      "post": {
        "tags": [
          "Assistants"
        ],
        "summary": "Reserve an assistant's name and machine size (nothing starts until its inputs arrive)",
        "description": "Claims the name and the RAM and storage the recipe needs, so a later\nsetup can never end in \"does not fit\". Send the inputs with\n`PUT /agents/{name}/inputs`, or let the owner fill them in a browser\nat `secrets_url` so the caller never holds them. On the free plan\nthe assistant is destroyed 24 hours after it is created.\n",
        "x-audience": "frontend",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "catalog": {
                    "type": "string",
                    "description": "A catalog name from GET /agents."
                  },
                  "name": {
                    "type": "string"
                  }
                },
                "required": [
                  "catalog",
                  "name"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Reserved",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "name": {
                      "type": "string"
                    },
                    "catalog": {
                      "type": "string"
                    },
                    "version": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string"
                    },
                    "needed": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/AgentInput"
                      }
                    },
                    "selections": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      }
                    },
                    "secrets_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "One-time link where the owner enters the inputs."
                    },
                    "expires_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "notice": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "name",
                    "catalog",
                    "version",
                    "status",
                    "needed"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Does not fit the plan's pool (POOL_MEMORY_LIMIT / POOL_VOLUME_LIMIT, with upgrade), or the account is under review (UNDER_REVIEW)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such catalog entry",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Name already yours (agent_name_yours, with status), taken (agent_name_taken), or too many unfinished setups (SETUP_LIMIT)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/agents/{name}/inputs": {
      "put": {
        "tags": [
          "Assistants"
        ],
        "summary": "Send an assistant's inputs and start it (or apply new ones to a running one)",
        "x-audience": "frontend",
        "parameters": [
          {
            "$ref": "#/components/parameters/AgentName"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "values": {
                    "type": "object",
                    "additionalProperties": {
                      "type": "string"
                    },
                    "description": "Input key to value, for the keys in `needed`. Stored encrypted."
                  },
                  "choices": {
                    "type": "object",
                    "additionalProperties": {
                      "type": "string"
                    },
                    "description": "Answers to the pick-one questions in `selections`."
                  },
                  "extras": {
                    "type": "object",
                    "additionalProperties": {
                      "type": "string"
                    },
                    "description": "Opt-in integration variables from the recipe's extras table."
                  }
                },
                "required": [
                  "values"
                ]
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Accepted; poll GET /agents/hosts/{name}",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "name": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string"
                    },
                    "reconfigure": {
                      "type": "boolean",
                      "description": "True when the assistant already had a machine and is applying the change in place."
                    },
                    "note": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "name",
                    "status",
                    "reconfigure",
                    "note"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Already being set up (CONFLICT), or still starting (OPERATION_IN_PROGRESS)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/agents/requests/{token}": {
      "get": {
        "tags": [
          "Assistants"
        ],
        "summary": "What a one-time setup link asks for (keys only, never values)",
        "x-audience": "frontend",
        "parameters": [
          {
            "$ref": "#/components/parameters/SetupToken"
          }
        ],
        "responses": {
          "200": {
            "description": "The form to render",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "host": {
                      "type": "string"
                    },
                    "title": {
                      "type": "string"
                    },
                    "summary": {
                      "type": "string"
                    },
                    "needed": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/AgentInput"
                      }
                    },
                    "selections": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      }
                    },
                    "extras": {
                      "type": "object"
                    }
                  },
                  "required": [
                    "host",
                    "needed"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "410": {
            "description": "The link was already used; the body says what happened since",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "host": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string"
                    },
                    "reconfigure": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "host",
                    "status",
                    "reconfigure"
                  ]
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Assistants"
        ],
        "summary": "Submit a one-time setup link's form (spends the link)",
        "x-audience": "frontend",
        "parameters": [
          {
            "$ref": "#/components/parameters/SetupToken"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "values": {
                    "type": "object",
                    "additionalProperties": {
                      "type": "string"
                    }
                  },
                  "choices": {
                    "type": "object",
                    "additionalProperties": {
                      "type": "string"
                    }
                  },
                  "extras": {
                    "type": "object",
                    "additionalProperties": {
                      "type": "string"
                    }
                  }
                },
                "required": [
                  "values"
                ]
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Accepted; the assistant is starting or applying the change",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string"
                    },
                    "note": {
                      "type": "string"
                    },
                    "host": {
                      "type": "string"
                    },
                    "reconfigure": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "status",
                    "note",
                    "host",
                    "reconfigure"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "410": {
            "description": "The link was already used",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/agents/hosts": {
      "get": {
        "tags": [
          "Assistants"
        ],
        "summary": "List this account's assistants",
        "x-audience": "frontend",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "hosts": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/AgentHost"
                      }
                    }
                  },
                  "required": [
                    "hosts"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/agents/hosts/{name}": {
      "get": {
        "tags": [
          "Assistants"
        ],
        "summary": "One assistant's state",
        "description": "Never returns the control-panel password; see `GET .../panel/password`.",
        "x-audience": "frontend",
        "parameters": [
          {
            "$ref": "#/components/parameters/AgentName"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AgentHost"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "delete": {
        "tags": [
          "Assistants"
        ],
        "summary": "Destroy an assistant, its machine and every stored input",
        "x-audience": "frontend",
        "parameters": [
          {
            "$ref": "#/components/parameters/AgentName"
          }
        ],
        "responses": {
          "200": {
            "description": "Destroyed",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string",
                      "enum": [
                        "destroyed"
                      ]
                    },
                    "name": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "status",
                    "name"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Held for review (UNDER_REVIEW)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Another change is running, or it is still being created (OPERATION_IN_PROGRESS)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "$ref": "#/components/responses/UpstreamError"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/agents/hosts/{name}/logs": {
      "get": {
        "tags": [
          "Assistants"
        ],
        "summary": "The end of the assistant's log, with anything credential-shaped redacted",
        "x-audience": "frontend",
        "parameters": [
          {
            "$ref": "#/components/parameters/AgentName"
          },
          {
            "name": "lines",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1
            },
            "description": "How many lines to read (capped server-side)."
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "name": {
                      "type": "string"
                    },
                    "lines": {
                      "type": "integer"
                    },
                    "log": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "name",
                    "lines",
                    "log"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "The assistant has no machine yet",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "$ref": "#/components/responses/UpstreamError"
          }
        }
      }
    },
    "/agents/hosts/{name}/ssh": {
      "post": {
        "tags": [
          "Assistants"
        ],
        "summary": "Open an SSH session to the assistant's machine (for the owner's own ssh client)",
        "description": "Registers the given public key for sixty seconds and returns a\none-shot tunnel. The CLI (`innstance agents ssh`) drives this.\n",
        "x-audience": "cli",
        "parameters": [
          {
            "$ref": "#/components/parameters/AgentName"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "public_key": {
                    "type": "string",
                    "description": "An OpenSSH public key."
                  }
                },
                "required": [
                  "public_key"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Session",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "session_id": {
                      "type": "string"
                    },
                    "stream_url": {
                      "type": "string",
                      "format": "uri"
                    },
                    "token": {
                      "type": "string"
                    },
                    "user": {
                      "type": "string"
                    },
                    "key_valid_seconds": {
                      "type": "integer",
                      "example": 60
                    }
                  },
                  "required": [
                    "session_id",
                    "stream_url",
                    "token",
                    "user",
                    "key_valid_seconds"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "The assistant is not running — OPERATION_IN_PROGRESS (retryable) while its launch is advancing, CONFLICT while it waits for its inputs or after a failed launch",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "502": {
            "$ref": "#/components/responses/UpstreamError"
          }
        }
      }
    },
    "/agents/hosts/{name}/exec": {
      "post": {
        "tags": [
          "Assistants"
        ],
        "summary": "Run one command on the assistant's machine (or inside the assistant with in_agent)",
        "x-audience": "agent",
        "parameters": [
          {
            "$ref": "#/components/parameters/AgentName"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "cmd": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "example": [
                      "docker",
                      "ps"
                    ]
                  },
                  "in_agent": {
                    "type": "boolean",
                    "description": "Run inside the assistant's container instead of on the host."
                  },
                  "timeout_seconds": {
                    "type": "integer",
                    "description": "Capped server-side."
                  }
                },
                "required": [
                  "cmd"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The command ran; check exit_code",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "stdout": {
                      "type": "string"
                    },
                    "stderr": {
                      "type": "string"
                    },
                    "exit_code": {
                      "type": "integer"
                    },
                    "status": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "stdout",
                    "stderr",
                    "exit_code",
                    "status"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "The assistant is not running — OPERATION_IN_PROGRESS (retryable) while its launch is advancing, CONFLICT while it waits for its inputs or after a failed launch",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "502": {
            "description": "The command channel did not take the command (UPSTREAM_ERROR, retryable: it did not run),\nor took it and its result could not be read (OUTCOME_UNKNOWN: it may have run; check\nbefore sending it again).\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "504": {
            "description": "The command ran out of time and may still be running (EXEC_TIMEOUT, not retryable); start long work detached and poll",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/agents/hosts/{name}/export": {
      "post": {
        "tags": [
          "Assistants"
        ],
        "summary": "Prepare a backup of the assistant's state (the download is metered traffic)",
        "x-audience": "frontend",
        "parameters": [
          {
            "$ref": "#/components/parameters/AgentName"
          }
        ],
        "responses": {
          "200": {
            "description": "Prepared; download with GET and the token",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "host": {
                      "type": "string"
                    },
                    "size_bytes": {
                      "type": "integer"
                    },
                    "sha256": {
                      "type": "string"
                    },
                    "token": {
                      "type": "string",
                      "description": "One-time download token."
                    },
                    "expires_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "metered": {
                      "type": "boolean"
                    },
                    "warning": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  },
                  "required": [
                    "host",
                    "size_bytes",
                    "sha256",
                    "token",
                    "expires_at",
                    "metered"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "description": "Larger than the remaining traffic allowance and credit",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Not running, or another backup is being prepared (OPERATION_IN_PROGRESS)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "$ref": "#/components/responses/UpstreamError"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      },
      "get": {
        "tags": [
          "Assistants"
        ],
        "summary": "Download a prepared backup",
        "x-audience": "frontend",
        "parameters": [
          {
            "$ref": "#/components/parameters/AgentName"
          },
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The token POST returned. It is what makes this billable GET safe for a cookie-authenticated browser."
          }
        ],
        "responses": {
          "200": {
            "description": "The archive",
            "content": {
              "application/gzip": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "description": "Larger than the remaining traffic allowance and credit",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "The token was already used or expired, or the preparation expired",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "$ref": "#/components/responses/UpstreamError"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/agents/hosts/{name}/reconfigure": {
      "post": {
        "tags": [
          "Assistants"
        ],
        "summary": "Mint a one-time link where the owner changes the assistant's settings",
        "x-audience": "frontend",
        "parameters": [
          {
            "$ref": "#/components/parameters/AgentName"
          }
        ],
        "responses": {
          "201": {
            "description": "Link minted (a first-setup link when the assistant was never built)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "name": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string"
                    },
                    "secrets_url": {
                      "type": "string",
                      "format": "uri"
                    },
                    "note": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "name",
                    "status",
                    "secrets_url",
                    "note"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "An earlier change is still applying (OPERATION_IN_PROGRESS)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/agents/hosts/{name}/panel": {
      "post": {
        "tags": [
          "Assistants"
        ],
        "summary": "Make the assistant's control panel private (owner only) or reachable",
        "x-audience": "frontend",
        "parameters": [
          {
            "$ref": "#/components/parameters/AgentName"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "private": {
                    "type": "boolean"
                  }
                },
                "required": [
                  "private"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "name": {
                      "type": "string"
                    },
                    "panel_private": {
                      "type": "boolean"
                    },
                    "note": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "name",
                    "panel_private",
                    "note"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/agents/hosts/{name}/panel/password": {
      "get": {
        "tags": [
          "Assistants"
        ],
        "summary": "Reveal the control-panel password until the owner confirms saving it",
        "description": "Idempotent: every call returns the current password until\n`POST .../panel/password/shown` records that the owner saved it;\nafter that it returns `shown: true` and no password.\n",
        "x-audience": "frontend",
        "parameters": [
          {
            "$ref": "#/components/parameters/AgentName"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "has_password": {
                      "type": "boolean"
                    },
                    "shown": {
                      "type": "boolean"
                    },
                    "password": {
                      "type": "string"
                    },
                    "note": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "has_password"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Still being built — OPERATION_IN_PROGRESS (retryable) while its launch is advancing, CONFLICT otherwise",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "$ref": "#/components/responses/UpstreamError"
          }
        }
      }
    },
    "/agents/hosts/{name}/panel/password/shown": {
      "post": {
        "tags": [
          "Assistants"
        ],
        "summary": "Record that the owner saved the panel password (idempotent)",
        "x-audience": "frontend",
        "parameters": [
          {
            "$ref": "#/components/parameters/AgentName"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "shown": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    }
                  },
                  "required": [
                    "shown"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "502": {
            "$ref": "#/components/responses/UpstreamError"
          }
        }
      }
    },
    "/agents/hosts/{name}/panel/approve": {
      "post": {
        "tags": [
          "Assistants"
        ],
        "summary": "Approve the owner's browser on a panel that approves devices",
        "x-audience": "frontend",
        "parameters": [
          {
            "$ref": "#/components/parameters/AgentName"
          }
        ],
        "responses": {
          "200": {
            "description": "Approved, or nothing to approve",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "approved": {
                      "type": "boolean"
                    },
                    "note": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "approved",
                    "note"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Not up yet: OPERATION_IN_PROGRESS (retryable) while its launch is\nadvancing, CONFLICT while it waits for its inputs or after a\nfailed launch. Or more than one browser is waiting and the right\none cannot be told apart: that body is `{approved: false, waiting,\nnote}`, a state report with no error.\n",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "502": {
            "$ref": "#/components/responses/UpstreamError"
          }
        }
      }
    },
    "/agents/hosts/{name}/whatsapp": {
      "get": {
        "tags": [
          "Assistants"
        ],
        "summary": "Whether WhatsApp is linked (and repair the allowlist if it was linked elsewhere)",
        "x-audience": "frontend",
        "parameters": [
          {
            "$ref": "#/components/parameters/AgentName"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "linked": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "linked"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "The assistant does not use WhatsApp (CONFLICT), or is not up yet: OPERATION_IN_PROGRESS (retryable) while its launch is advancing, CONFLICT while it waits for its inputs or after a failed launch",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "$ref": "#/components/responses/UpstreamError"
          }
        }
      }
    },
    "/agents/hosts/{name}/whatsapp/pair": {
      "post": {
        "tags": [
          "Assistants"
        ],
        "summary": "Start linking WhatsApp",
        "x-audience": "frontend",
        "parameters": [
          {
            "$ref": "#/components/parameters/AgentName"
          }
        ],
        "responses": {
          "200": {
            "description": "Linking session started; poll with GET and the pairing id",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "pairing_id": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "status"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "The assistant does not use WhatsApp (CONFLICT), or is not up yet: OPERATION_IN_PROGRESS (retryable) while its launch is advancing, CONFLICT while it waits for its inputs or after a failed launch",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "$ref": "#/components/responses/UpstreamError"
          }
        }
      },
      "get": {
        "tags": [
          "Assistants"
        ],
        "summary": "Poll a WhatsApp linking session (the code to scan, as a PNG and as its payload)",
        "x-audience": "frontend",
        "parameters": [
          {
            "$ref": "#/components/parameters/AgentName"
          },
          {
            "name": "id",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "The pairing id POST returned."
          }
        ],
        "responses": {
          "200": {
            "description": "Session state; `status` is `expired` when it is over",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string"
                    },
                    "phone": {
                      "type": "string"
                    },
                    "qr": {
                      "type": "string",
                      "description": "data:image/png;base64,..."
                    },
                    "qr_payload": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "status"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "The assistant does not use WhatsApp (CONFLICT), or is not up yet: OPERATION_IN_PROGRESS (retryable) while its launch is advancing, CONFLICT while it waits for its inputs or after a failed launch",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "$ref": "#/components/responses/UpstreamError"
          }
        }
      }
    },
    "/agents/hosts/{name}/whatsapp/apply": {
      "post": {
        "tags": [
          "Assistants"
        ],
        "summary": "Finish linking WhatsApp (the assistant stores the session and restarts)",
        "x-audience": "frontend",
        "parameters": [
          {
            "$ref": "#/components/parameters/AgentName"
          },
          {
            "name": "id",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`ok: true` when linked and restarted; `ok: false` with `problem` otherwise",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "restarted": {
                      "type": "boolean"
                    },
                    "problem": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "ok"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "The assistant does not use WhatsApp",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "$ref": "#/components/responses/UpstreamError"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "API token minted by `/auth/google/cli` or `POST /auth/token`. Sent as `Authorization: Bearer \u003ctoken\u003e`."
      }
    },
    "parameters": {
      "AppName": {
        "name": "name",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string"
        },
        "description": "App name (also the subdomain label)."
      },
      "TokenId": {
        "name": "tokenId",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string"
        }
      },
      "VolName": {
        "name": "volName",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string"
        }
      },
      "NoRestart": {
        "name": "no_restart",
        "in": "query",
        "schema": {
          "type": "string",
          "enum": [
            "true"
          ]
        },
        "description": "Skip the machine restart that normally applies env/secret changes immediately."
      },
      "SiteName": {
        "name": "name",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string"
        },
        "description": "Site name (also its address label)."
      },
      "AgentName": {
        "name": "name",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string"
        },
        "description": "Assistant name (also its address label)."
      },
      "Hostname": {
        "name": "hostname",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string"
        },
        "description": "A custom domain, e.g. www.example.com."
      },
      "Nudge": {
        "name": "nudge",
        "in": "query",
        "schema": {
          "type": "string",
          "enum": [
            "false"
          ]
        },
        "description": "`false` skips asking the certificate provider to re-check (the re-check is also limited per window)."
      },
      "SetupToken": {
        "name": "token",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string"
        },
        "description": "The one-time token in a setup link."
      },
      "UploadMachine": {
        "name": "machine",
        "in": "query",
        "required": true,
        "schema": {
          "type": "string"
        },
        "description": "The machine the upload targets."
      }
    },
    "responses": {
      "BadRequest": {
        "description": "The request is invalid (INVALID_REQUEST, or a specific code such as NAME_NOT_ALLOWED)",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Unauthorized": {
        "description": "Missing, unknown or expired credential (UNAUTHENTICATED)",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "NotFound": {
        "description": "Not found on this account (NOT_FOUND)",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Conflict": {
        "description": "Conflicts with the resource's state (CONFLICT), or another change is running (OPERATION_IN_PROGRESS — send it again shortly)",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "RateLimited": {
        "description": "Too many requests (RATE_LIMIT); wait for Retry-After",
        "headers": {
          "Retry-After": {
            "schema": {
              "type": "integer"
            },
            "description": "Seconds to wait before sending it again."
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "ServerError": {
        "description": "Internal error (INTERNAL_ERROR)",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "UpstreamError": {
        "description": "A provider failed (UPSTREAM_ERROR); OUTCOME_UNKNOWN when the request may have taken effect",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Unavailable": {
        "description": "Briefly unavailable (UNAVAILABLE), or not enabled on this deployment (NOT_ENABLED — do not retry)",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "description": "The envelope every JSON error uses. The catalog of codes — what each\nmeans, whether a retry can succeed, what to do — is served at\n/docs/errors (/docs/errors.json for machines). Some codes add fields\nof their own (upgrade, retry_after, over_pool_until, moderation, ...).\n",
        "properties": {
          "error": {
            "type": "string",
            "description": "The human sentence; print it as-is."
          },
          "code": {
            "type": "string",
            "description": "Stable once sent; branch on this. A refusal with no code of its\nown carries the generic code for its HTTP status (INVALID_REQUEST,\nUNAUTHENTICATED, FORBIDDEN, NOT_FOUND, CONFLICT, RATE_LIMIT,\nINTERNAL_ERROR, UPSTREAM_ERROR, UNAVAILABLE, ...).\n"
          },
          "retryable": {
            "type": "boolean",
            "description": "True when the same request, sent again after a pause, can succeed unchanged. Honour Retry-After when set."
          },
          "docs_url": {
            "type": "string",
            "format": "uri",
            "description": "The catalog entry for this code."
          },
          "retry": {
            "type": "string",
            "description": "Present on a few errors (e.g. DELETE_FAILED) — a human-readable retry hint."
          }
        },
        "required": [
          "error",
          "code",
          "retryable",
          "docs_url"
        ]
      },
      "PaymentNotCreditedError": {
        "description": "An error from a top-up route. When the code is PAYMENT_NOT_CREDITED the\npayment settled on-chain and was not credited, and `tx_hash` names it.\n",
        "allOf": [
          {
            "$ref": "#/components/schemas/Error"
          },
          {
            "type": "object",
            "properties": {
              "tx_hash": {
                "type": "string",
                "description": "The settled transaction; present with PAYMENT_NOT_CREDITED."
              }
            }
          }
        ]
      },
      "PoolLockedError": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string",
            "example": "another operation in progress, retry shortly"
          },
          "code": {
            "type": "string",
            "enum": [
              "POOL_LOCKED"
            ]
          },
          "retryable": {
            "type": "boolean",
            "enum": [
              true
            ]
          },
          "docs_url": {
            "type": "string",
            "format": "uri"
          }
        },
        "required": [
          "error",
          "code",
          "retryable",
          "docs_url"
        ]
      },
      "OverPoolError": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string"
          },
          "code": {
            "type": "string",
            "enum": [
              "OVER_POOL"
            ]
          },
          "retryable": {
            "type": "boolean",
            "enum": [
              false
            ]
          },
          "docs_url": {
            "type": "string",
            "format": "uri"
          },
          "over_pool_until": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "error",
          "code",
          "retryable",
          "docs_url",
          "over_pool_until"
        ]
      },
      "TierViolationError": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string"
          },
          "code": {
            "type": "string",
            "enum": [
              "FREE_TIER_DISABLED",
              "FREE_TIER_FULL",
              "FREE_COMPUTE_FULL",
              "APP_LIMIT",
              "CPU_LIMIT",
              "CPU_KIND_LIMIT",
              "POOL_MEMORY_LIMIT",
              "VOLUMES_NOT_AVAILABLE",
              "POOL_VOLUME_LIMIT",
              "AUTOSCALE_LIMIT",
              "DOMAINS_NOT_AVAILABLE",
              "DOMAIN_LIMIT",
              "ALWAYS_ON_PERFORMANCE",
              "ALWAYS_ON_COST_LIMIT",
              "SITE_LIMIT",
              "LIMITS_UNAVAILABLE"
            ]
          },
          "retryable": {
            "type": "boolean"
          },
          "docs_url": {
            "type": "string",
            "format": "uri"
          },
          "upgrade": {
            "type": "string",
            "description": "Suggested next tier (empty on the top tier)."
          },
          "fixes": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Always-on margin refusals only: each way out, one per entry. `error` already says them in prose."
          },
          "current_cents": {
            "type": "integer",
            "description": "Always-on margin refusals only: the monthly always-on cost the change would reach (performance cost for ALWAYS_ON_PERFORMANCE)."
          },
          "limit_cents": {
            "type": "integer",
            "description": "ALWAYS_ON_COST_LIMIT only: the plan's always-on budget per month."
          }
        },
        "required": [
          "error",
          "code",
          "retryable",
          "docs_url"
        ]
      },
      "HealthOK": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "ok"
            ]
          },
          "payments_network": {
            "type": "string",
            "enum": [
              "base",
              "base-sepolia"
            ],
            "description": "The chain the USDC payment rails settle on. Absent when the\ndeployment takes no USDC payments.\n"
          },
          "payments_paused": {
            "type": "boolean",
            "description": "Present (true) only while the payments check refuses the USDC\nrails: the payment settings disagree and IMPOSSIBLE_PAYMENTS_CHECK\nis enforce. The reasons are on the admin page, never here.\n"
          }
        },
        "required": [
          "status"
        ]
      },
      "HealthError": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "error"
            ]
          },
          "detail": {
            "type": "string"
          }
        },
        "required": [
          "status",
          "detail"
        ]
      },
      "Me": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "email": {
            "type": "string",
            "format": "email"
          },
          "plan_tier": {
            "type": "string",
            "enum": [
              "free",
              "hobby",
              "pro",
              "team"
            ]
          },
          "over_pool": {
            "type": "boolean"
          },
          "over_pool_until": {
            "type": "string",
            "description": "RFC3339, or empty string when not set."
          }
        },
        "required": [
          "id",
          "email",
          "plan_tier",
          "over_pool",
          "over_pool_until"
        ]
      },
      "TokenSummary": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "prefix": {
            "type": "string",
            "example": "imp_5f2c1a9b..."
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "last_used_at": {
            "type": "string",
            "format": "date-time"
          },
          "access": {
            "type": "string",
            "example": "read publish; sites: menu",
            "description": "\"full access\" for a token with no limits."
          },
          "scopes": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "apps": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "sites": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        },
        "required": [
          "id",
          "name",
          "prefix",
          "created_at",
          "access"
        ]
      },
      "AppSummary": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "region": {
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "internal_port": {
            "type": "integer"
          },
          "url": {
            "type": "string"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "domains": {
            "type": [
              "array",
              "null"
            ],
            "description": "Same list as GET /apps/{name}/domains (list responses only); null when it could not be read.",
            "items": {
              "$ref": "#/components/schemas/Domain"
            }
          }
        },
        "required": [
          "id",
          "name",
          "region",
          "status",
          "internal_port",
          "url",
          "created_at"
        ]
      },
      "AppDetail": {
        "allOf": [
          {
            "$ref": "#/components/schemas/AppSummary"
          },
          {
            "type": "object",
            "properties": {
              "cpu_kind": {
                "type": "string",
                "enum": [
                  "shared",
                  "performance"
                ]
              },
              "cpus": {
                "type": "integer"
              },
              "memory_mb": {
                "type": "integer"
              },
              "supervised": {
                "type": "boolean",
                "description": "Created with supervised start: runner deploys boot through the overlay init on a /data volume, and the app sleeps when idle once run_cmd is set (unless deployed always-on). Stamped at creation and never changed."
              },
              "run_cmd": {
                "type": "string",
                "description": "The declared start command the runner's supervisor runs; empty when none is declared."
              }
            },
            "required": [
              "cpu_kind",
              "cpus",
              "memory_mb"
            ]
          }
        ]
      },
      "AppCreateRequest": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "Subdomain syntax: lowercase alphanumeric + hyphens, 1-40 chars, not on the reserved-word list."
          },
          "region": {
            "type": "string",
            "description": "Region code; defaults to iad.",
            "enum": [
              "iad",
              "ewr",
              "ord",
              "dfw",
              "lax",
              "sjc",
              "yyz",
              "gru",
              "lhr",
              "fra",
              "ams",
              "cdg",
              "arn",
              "nrt",
              "sin",
              "syd",
              "bom",
              "jnb"
            ]
          },
          "cpu_kind": {
            "type": "string",
            "enum": [
              "shared",
              "performance"
            ],
            "description": "Defaults to \"shared\"."
          },
          "cpus": {
            "type": "integer",
            "enum": [
              1,
              2,
              4,
              8
            ],
            "description": "Defaults to 1."
          },
          "memory_mb": {
            "type": "integer",
            "enum": [
              256,
              512,
              1024,
              2048,
              4096,
              8192
            ],
            "description": "Defaults to 256."
          },
          "autoscale_min": {
            "type": "integer",
            "minimum": 0
          },
          "autoscale_max": {
            "type": "integer",
            "minimum": 0,
            "description": "Defaults to 1."
          },
          "concurrency_target": {
            "type": "integer",
            "minimum": 0,
            "description": "Defaults to 25."
          },
          "storage": {
            "type": "string",
            "enum": [
              "",
              "local"
            ]
          },
          "autostop": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "Accepted but has NO effect on app-create (autostop is a per-deploy service flag). Present so a non-bool value is rejected at the edge rather than silently ignored."
          },
          "volumes": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "name": {
                  "type": "string"
                },
                "size_gb": {
                  "type": "integer"
                },
                "mount_path": {
                  "type": "string"
                }
              }
            }
          }
        },
        "required": [
          "name"
        ]
      },
      "AppCreateResponse": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "region": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "created"
            ]
          },
          "url": {
            "type": "string"
          },
          "supervised": {
            "type": "boolean",
            "description": "Created with supervised start (see AppDetail.supervised)."
          }
        },
        "required": [
          "id",
          "name",
          "region",
          "status",
          "url"
        ]
      },
      "AppUpdateRequest": {
        "type": "object",
        "properties": {
          "cpu_kind": {
            "type": "string",
            "enum": [
              "shared",
              "performance"
            ]
          },
          "cpus": {
            "type": "integer",
            "enum": [
              1,
              2,
              4,
              8
            ]
          },
          "memory_mb": {
            "type": "integer",
            "enum": [
              256,
              512,
              1024,
              2048,
              4096,
              8192
            ]
          },
          "internal_port": {
            "type": "integer",
            "minimum": 1,
            "maximum": 65535
          },
          "autoscale_min": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0
          },
          "autoscale_max": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0
          },
          "concurrency_target": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0
          }
        },
        "description": "At least one field is required. autoscale_min/autoscale_max/concurrency_target are pointer-typed server-side — omit to leave unchanged, 0 is a valid explicit value."
      },
      "ApplyResult": {
        "type": "object",
        "properties": {
          "applied": {
            "type": "integer"
          },
          "failed_machines": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        },
        "required": [
          "applied",
          "failed_machines"
        ]
      },
      "DeploymentSummary": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "image": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "building",
              "live",
              "partial",
              "failed",
              "cancelled"
            ]
          },
          "error": {
            "type": "string"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "finished_at": {
            "type": "string",
            "format": "date-time"
          },
          "failed_machines": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        },
        "required": [
          "id",
          "image",
          "status",
          "created_at"
        ]
      },
      "DeploymentDetail": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "image": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "building",
              "live",
              "partial",
              "failed",
              "cancelled"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "url": {
            "type": "string"
          },
          "failed_machines": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "error": {
            "type": "string"
          },
          "finished_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "image",
          "status",
          "created_at",
          "url"
        ]
      },
      "RunnerDeployRequest": {
        "type": "object",
        "properties": {
          "port": {
            "type": "integer",
            "description": "Overrides the app's internal_port for this deploy."
          },
          "env": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "secrets": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "autostop": {
            "type": "boolean",
            "description": "false = always-on; true opts into scale-to-zero. Omitted: always-on for apps created before supervised start; for apps created with it, sleep when idle once run_cmd is set (suspend up to 2 GB, stop above)."
          },
          "min_machines": {
            "type": "integer",
            "description": "Minimum machines the autostop policy keeps running."
          },
          "run_cmd": {
            "type": "string",
            "description": "Start command, run from / in the foreground with bash -c. Where the platform supervises it (supervised start is on), it starts at every boot and again after a crash, with output in /tmp/app.log; otherwise a new one is ignored with a notice (an app that already has one keeps it). Omit to keep the stored command; empty string clears it. Must stay in the foreground (no trailing '\u0026', no setsid without --wait), and literal secrets assigned inline (TOKEN=...) are refused: it is shown in plain text, so secrets go in `secrets`."
          },
          "always_on": {
            "type": "boolean",
            "description": "true keeps the machine running when idle (same as autostop false). Only web requests wake a sleeping app, so bots and workers that serve none need it."
          }
        }
      },
      "DeployResult": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Deployment ID."
          },
          "status": {
            "type": "string",
            "description": "e.g. live, partial, failed."
          },
          "url": {
            "type": "string"
          },
          "machine_id": {
            "type": "string"
          },
          "machines": {
            "type": "integer"
          },
          "autoscale": {
            "type": "boolean"
          },
          "failed_machines": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Present when status is \"partial\" — the primary machine updated but ≥1 replica failed all rolling-update retries."
          },
          "supervised": {
            "type": "boolean",
            "description": "Runner deploys: the app was created with supervised start."
          },
          "when_idle": {
            "type": "string",
            "enum": [
              "always_on",
              "suspend",
              "stop"
            ],
            "description": "Runner deploys: what happens when traffic stops. suspend keeps memory (machines up to 2 GB); stop does not."
          },
          "run_cmd": {
            "type": "string",
            "description": "Runner deploys: the start command the platform runs, when it runs one."
          },
          "run_log": {
            "type": "string",
            "description": "Runner deploys: where the start command's output goes."
          },
          "notices": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Agent-facing advice about this deploy, to show as written."
          }
        },
        "required": [
          "id",
          "status",
          "url",
          "machine_id",
          "machines",
          "autoscale"
        ]
      },
      "SetResult": {
        "type": "object",
        "properties": {
          "set": {
            "type": "integer",
            "description": "Count of keys written in this request."
          },
          "restarted": {
            "type": "boolean"
          },
          "size_kept": {
            "$ref": "#/components/schemas/SizeKept"
          }
        },
        "required": [
          "set",
          "restarted"
        ]
      },
      "RemoveResult": {
        "type": "object",
        "properties": {
          "removed": {
            "type": "string"
          },
          "restarted": {
            "type": "boolean"
          },
          "size_kept": {
            "$ref": "#/components/schemas/SizeKept"
          }
        },
        "required": [
          "removed",
          "restarted"
        ]
      },
      "SizeKept": {
        "type": "object",
        "description": "Present only when a restart kept the machine's current size instead of the stored one, because the always-on margin guard would refuse it (ALWAYS_ON_PERFORMANCE / ALWAYS_ON_COST_LIMIT, with fixes) or could not check it (LIMITS_UNAVAILABLE). The write itself landed.",
        "properties": {
          "error": {
            "type": "string"
          },
          "code": {
            "type": "string",
            "enum": [
              "ALWAYS_ON_PERFORMANCE",
              "ALWAYS_ON_COST_LIMIT",
              "LIMITS_UNAVAILABLE"
            ]
          },
          "fixes": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "upgrade": {
            "type": "string"
          },
          "current_cents": {
            "type": "integer"
          },
          "limit_cents": {
            "type": "integer"
          }
        },
        "required": [
          "error",
          "code"
        ]
      },
      "Volume": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "size_gb": {
            "type": "integer"
          },
          "mount_path": {
            "type": "string"
          },
          "region": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "created",
              "pending",
              "ready",
              "orphan"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "Absent on the POST create response."
          }
        },
        "required": [
          "id",
          "name",
          "size_gb",
          "mount_path",
          "region",
          "status"
        ]
      },
      "Domain": {
        "type": "object",
        "properties": {
          "hostname": {
            "type": "string"
          },
          "is_custom": {
            "type": "boolean"
          },
          "tls_status": {
            "type": "string",
            "description": "\"wildcard\" for the auto-provisioned default hostname; otherwise the live certificate-provisioning status (passthrough; values are not enumerated by this contract — treat as opaque)."
          }
        },
        "required": [
          "hostname",
          "is_custom",
          "tls_status"
        ]
      },
      "HealthDetail": {
        "type": "object",
        "properties": {
          "app": {
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "healthy": {
            "type": "boolean"
          },
          "machines_total": {
            "type": "integer"
          },
          "machines_running": {
            "type": "integer"
          },
          "machines": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string"
                },
                "state": {
                  "type": "string"
                },
                "region": {
                  "type": "string"
                },
                "cpu_kind": {
                  "type": "string"
                },
                "cpus": {
                  "type": "integer"
                },
                "memory_mb": {
                  "type": "integer"
                }
              },
              "required": [
                "id",
                "state",
                "region",
                "cpu_kind",
                "cpus",
                "memory_mb"
              ]
            }
          },
          "uptime_seconds": {
            "type": "number"
          },
          "uptime_display": {
            "type": "string",
            "example": "3.2 hours"
          },
          "http": {
            "type": "object",
            "properties": {
              "total_requests": {
                "type": "number"
              },
              "error_requests": {
                "type": "number",
                "description": "5xx count."
              },
              "error_rate": {
                "type": "string",
                "example": "0.0%"
              },
              "avg_response_ms": {
                "type": "number"
              },
              "p99_response_ms": {
                "type": "number"
              }
            },
            "required": [
              "total_requests",
              "error_requests",
              "error_rate",
              "avg_response_ms",
              "p99_response_ms"
            ]
          }
        },
        "required": [
          "app",
          "status",
          "healthy",
          "machines_total",
          "machines_running",
          "machines",
          "uptime_seconds",
          "uptime_display",
          "http"
        ]
      },
      "AppDiagnosis": {
        "type": "object",
        "properties": {
          "app": {
            "type": "string"
          },
          "state": {
            "type": "string",
            "enum": [
              "answering",
              "waking",
              "down",
              "unknown"
            ],
            "description": "answering: something listens on the port. waking: starting, or a deployment is rolling. down: nothing answers until the owner acts. unknown: the evidence could not be read."
          },
          "reason": {
            "type": "string",
            "enum": [
              "listening",
              "no_machines",
              "paused",
              "deploy_in_progress",
              "starting",
              "stopped",
              "machine_failed",
              "nothing_listening",
              "wrong_port",
              "localhost_only",
              "unchecked"
            ]
          },
          "summary": {
            "type": "string",
            "description": "One or two sentences for the owner: what is wrong and what fixes it."
          },
          "next_step": {
            "type": "string",
            "description": "The CLI command to run next, when there is one.",
            "example": "innstance machines logs --app my-app"
          },
          "port": {
            "type": "integer",
            "description": "The port requests are routed to."
          },
          "machines": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string"
                },
                "state": {
                  "type": "string"
                },
                "region": {
                  "type": "string"
                },
                "port": {
                  "type": "integer"
                },
                "autostart": {
                  "type": "boolean"
                },
                "port_check": {
                  "type": "string",
                  "enum": [
                    "listening",
                    "localhost_only",
                    "not_listening",
                    "not_running",
                    "unavailable",
                    "not_checked"
                  ]
                },
                "other_ports": {
                  "type": "array",
                  "items": {
                    "type": "integer"
                  },
                  "description": "Ports something listens on instead of the configured one."
                },
                "updated_at": {
                  "type": "string"
                }
              },
              "required": [
                "id",
                "state",
                "port",
                "autostart",
                "port_check"
              ]
            }
          },
          "last_deploy": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "status": {
                "type": "string",
                "enum": [
                  "building",
                  "live",
                  "staged",
                  "partial",
                  "failed",
                  "cancelled"
                ],
                "description": "staged: a runner deployment whose machine is ready; the app process is started separately."
              },
              "error": {
                "type": "string"
              },
              "created_at": {
                "type": "string",
                "format": "date-time"
              },
              "finished_at": {
                "type": "string",
                "format": "date-time"
              }
            },
            "required": [
              "id",
              "status",
              "created_at"
            ]
          },
          "checked_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "app",
          "state",
          "reason",
          "summary",
          "port",
          "machines",
          "checked_at"
        ]
      },
      "BillingPlan": {
        "type": "object",
        "properties": {
          "tier": {
            "type": "string",
            "enum": [
              "free",
              "hobby",
              "pro",
              "team"
            ]
          },
          "price": {
            "type": "string",
            "example": "$15/mo"
          },
          "period": {
            "type": "string",
            "example": "2026-07-01"
          },
          "over_pool": {
            "type": "boolean"
          },
          "over_pool_until": {
            "type": "string",
            "description": "RFC3339, or empty string."
          },
          "recurring": {
            "type": "boolean",
            "description": "A recurring subscription renews this plan."
          },
          "scheduled_tier": {
            "type": "string",
            "description": "The plan a scheduled change moves to, or empty."
          },
          "scheduled_change_at": {
            "type": "string"
          },
          "limits": {
            "$ref": "#/components/schemas/PlanLimits"
          },
          "overage_rates": {
            "type": "object",
            "properties": {
              "volume_cents_per_gb_month": {
                "type": "integer"
              },
              "egress_cents_per_gb_na_eu": {
                "type": "integer"
              },
              "egress_cents_per_gb_apac": {
                "type": "integer"
              },
              "egress_cents_per_gb_other": {
                "type": "integer"
              }
            }
          },
          "usage": {
            "type": "object",
            "properties": {
              "apps": {
                "type": "integer"
              },
              "volumes": {
                "type": "integer"
              },
              "custom_domains": {
                "type": "integer"
              },
              "pool_memory_used_mb": {
                "type": "integer"
              },
              "pool_volume_used_gb": {
                "type": "integer"
              },
              "compute_hours": {
                "type": "string",
                "example": "12.3"
              },
              "bandwidth_gb": {
                "type": "string",
                "example": "1.05"
              },
              "estimated_cost_cents": {
                "type": "integer"
              },
              "apps_memory_mb": {
                "type": "integer"
              },
              "agents_memory_mb": {
                "type": "integer"
              },
              "app_volumes_gb": {
                "type": "integer"
              },
              "agents_disk_gb": {
                "type": "integer"
              }
            }
          },
          "usage_degraded": {
            "type": "boolean",
            "description": "Present (true) when a usage figure could not be read: a zero may not be a real zero."
          },
          "egress": {
            "type": "object",
            "description": "Traffic this period: used, included, remaining and credit (GB), and whether it is blocked."
          },
          "activation": {
            "type": "object",
            "description": "Free accounts paying toward a plan: what is banked and what is still needed."
          },
          "standing": {
            "type": "object",
            "description": "The account's plan standing (grace, debt, scheduled changes)."
          },
          "payments_network": {
            "$ref": "#/components/schemas/PaymentsNetwork"
          }
        },
        "required": [
          "tier",
          "price",
          "period",
          "over_pool",
          "over_pool_until",
          "limits",
          "overage_rates",
          "usage"
        ]
      },
      "BillingUsage": {
        "type": "object",
        "properties": {
          "period": {
            "type": "string"
          },
          "entries": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "app": {
                  "type": "string"
                },
                "type": {
                  "type": "string",
                  "enum": [
                    "compute_seconds",
                    "build_seconds",
                    "bandwidth_bytes"
                  ]
                },
                "quantity": {
                  "type": "number"
                },
                "unit": {
                  "type": "string"
                }
              },
              "required": [
                "app",
                "type",
                "quantity",
                "unit"
              ]
            }
          },
          "totals": {
            "type": "object",
            "additionalProperties": {
              "type": "object",
              "properties": {
                "total": {
                  "type": "number"
                },
                "unit": {
                  "type": "string"
                },
                "display": {
                  "type": "string"
                }
              }
            }
          }
        },
        "required": [
          "period",
          "entries",
          "totals"
        ]
      },
      "AppUsage": {
        "type": "object",
        "properties": {
          "app": {
            "type": "string"
          },
          "period": {
            "type": "string"
          },
          "usage": {
            "type": "object",
            "additionalProperties": {
              "type": "object",
              "properties": {
                "total": {
                  "type": "number"
                },
                "unit": {
                  "type": "string"
                },
                "display": {
                  "type": "string"
                }
              }
            }
          }
        },
        "required": [
          "app",
          "period",
          "usage"
        ]
      },
      "StoreSubscriptionStatus": {
        "type": "object",
        "description": "The account's current subscription state.",
        "properties": {
          "plan": {
            "type": "string",
            "enum": [
              "free",
              "hobby",
              "pro",
              "team"
            ]
          },
          "status": {
            "type": [
              "string",
              "null"
            ]
          },
          "cancelled": {
            "type": "boolean"
          }
        },
        "required": [
          "plan",
          "cancelled"
        ]
      },
      "Invoice": {
        "type": "object",
        "description": "One payment the account was charged.",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "paid",
              "failed",
              "pending"
            ]
          },
          "total_formatted": {
            "type": "string",
            "example": "$15.00",
            "description": "The amount charged, with its currency."
          },
          "invoiced_at": {
            "type": "string",
            "description": "When it settled (when it was created, while it has not)."
          },
          "description": {
            "type": "string",
            "example": "Pro plan",
            "description": "The plan it paid for; absent when the payment is not for a plan."
          }
        },
        "required": [
          "status",
          "total_formatted",
          "invoiced_at"
        ]
      },
      "TopupPreview": {
        "type": "object",
        "properties": {
          "deposit_address": {
            "type": "string"
          },
          "plan": {
            "type": "string",
            "enum": [
              "hobby",
              "pro",
              "team"
            ]
          },
          "monthly_price_usdc": {
            "type": "string",
            "example": "15.00"
          },
          "rate_days_per_dollar": {
            "type": "string",
            "example": "2.0000"
          },
          "minimum_credit_usdc": {
            "type": "string",
            "example": "0.03"
          },
          "bound_wallets": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "address": {
                  "type": "string"
                },
                "verified_at": {
                  "type": "string",
                  "format": "date-time"
                }
              },
              "required": [
                "address",
                "verified_at"
              ]
            }
          },
          "expires_at_current": {
            "type": "string",
            "description": "RFC3339, or empty string if the user has no active expiry."
          },
          "network": {
            "type": "string",
            "enum": [
              "base",
              "base-sepolia"
            ],
            "description": "The chain to send USDC on."
          },
          "network_name": {
            "type": "string",
            "example": "Base Sepolia (testnet)",
            "description": "The chain in words; a testnet always says so."
          },
          "testnet": {
            "type": "boolean",
            "description": "True when the USDC on this chain has no value."
          }
        },
        "required": [
          "deposit_address",
          "plan",
          "monthly_price_usdc",
          "rate_days_per_dollar",
          "minimum_credit_usdc",
          "bound_wallets",
          "expires_at_current",
          "network",
          "network_name",
          "testnet"
        ]
      },
      "PaymentsNetwork": {
        "type": "object",
        "description": "The chain the USDC payment rails run on. Present in /billing/plan only\nwhen the deployment takes USDC payments.\n",
        "properties": {
          "network": {
            "type": "string",
            "enum": [
              "base",
              "base-sepolia"
            ]
          },
          "name": {
            "type": "string",
            "example": "Base Sepolia"
          },
          "label": {
            "type": "string",
            "example": "Base Sepolia (testnet)"
          },
          "testnet": {
            "type": "boolean"
          },
          "paused": {
            "type": "boolean",
            "description": "True while the payments check refuses the USDC rails (enforce mode only; every quote and challenge then answers 503 payments_paused)."
          }
        },
        "required": [
          "network",
          "name",
          "label",
          "testnet",
          "paused"
        ]
      },
      "PaymentsPaused": {
        "type": "object",
        "description": "The USDC rails are refused: the chain, USDC contract, RPC, facilitator\nand deposit address do not agree (docs/payments-mainnet-checklist.md).\nNothing was charged.\n",
        "properties": {
          "error": {
            "type": "string"
          },
          "code": {
            "type": "string",
            "enum": [
              "payments_paused"
            ]
          }
        },
        "required": [
          "error",
          "code"
        ]
      },
      "TopupCredit": {
        "type": "object",
        "properties": {
          "address": {
            "type": "string"
          },
          "tx_hash": {
            "type": "string"
          },
          "amount_usdc": {
            "type": "string",
            "example": "5.000000"
          },
          "days_granted": {
            "type": "integer"
          },
          "block_number": {
            "type": "integer"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "address",
          "tx_hash",
          "amount_usdc",
          "days_granted",
          "created_at"
        ]
      },
      "X402Requirements": {
        "type": "object",
        "properties": {
          "scheme": {
            "type": "string",
            "enum": [
              "exact"
            ]
          },
          "network": {
            "type": "string",
            "example": "eip155:8453"
          },
          "amount": {
            "type": "string",
            "description": "Atomic USDC units (6 decimals)."
          },
          "maxAmountRequired": {
            "type": "string",
            "description": "Duplicates `amount` for v1-era clients."
          },
          "asset": {
            "type": "string",
            "description": "USDC contract address."
          },
          "payTo": {
            "type": "string",
            "description": "Platform deposit address."
          },
          "description": {
            "type": "string"
          },
          "maxTimeoutSeconds": {
            "type": "integer",
            "enum": [
              600
            ]
          },
          "extra": {
            "type": "object",
            "properties": {
              "name": {
                "type": "string",
                "enum": [
                  "USD Coin",
                  "USDC"
                ],
                "description": "USDC's EIP-712 domain name, as the chain's USDC contract\nreturns it from `name()`: \"USD Coin\" on Base mainnet\n(`eip155:8453`), \"USDC\" on Base Sepolia (`eip155:84532`).\nSign with exactly this name; a signature under the other\none is rejected by the contract.\n"
              },
              "version": {
                "type": "string",
                "enum": [
                  "2"
                ]
              }
            }
          }
        },
        "required": [
          "scheme",
          "network",
          "amount",
          "maxAmountRequired",
          "asset",
          "payTo",
          "description",
          "maxTimeoutSeconds",
          "extra"
        ]
      },
      "WalletBinding": {
        "type": "object",
        "properties": {
          "address": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "verified"
            ]
          },
          "deposit_address": {
            "type": "string"
          },
          "verify_amount_usdc": {
            "type": "string",
            "description": "Empty for bindings created under the current (immediate-verify) flow."
          },
          "verify_amount_atomic": {
            "type": "string"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          },
          "verified_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "address",
          "status",
          "deposit_address"
        ]
      },
      "Machine": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "state": {
            "type": "string",
            "example": "started"
          },
          "region": {
            "type": "string"
          },
          "cpu_kind": {
            "type": "string"
          },
          "cpus": {
            "type": "integer"
          },
          "memory_mb": {
            "type": "integer"
          },
          "created_at": {
            "type": "string"
          },
          "updated_at": {
            "type": "string"
          },
          "runner": {
            "type": "boolean",
            "description": "A runner-mode machine."
          },
          "wakes_for_commands": {
            "type": "boolean",
            "description": "The machine slept when idle (suspended or stopped) and exec, console start/input, push/write and pull wake it before they run. Absent otherwise."
          }
        },
        "required": [
          "id",
          "name",
          "state",
          "region",
          "cpu_kind",
          "cpus",
          "memory_mb",
          "created_at",
          "updated_at"
        ]
      },
      "ExecResult": {
        "type": "object",
        "properties": {
          "stdout": {
            "type": "string"
          },
          "stderr": {
            "type": "string"
          },
          "exit_code": {
            "type": "integer"
          }
        },
        "required": [
          "stdout",
          "stderr",
          "exit_code"
        ]
      },
      "ErrorCatalogEntry": {
        "type": "object",
        "properties": {
          "code": {
            "type": "string"
          },
          "status": {
            "type": "array",
            "items": {
              "type": "integer"
            }
          },
          "retryable": {
            "type": "boolean"
          },
          "summary": {
            "type": "string"
          },
          "action": {
            "type": "string"
          },
          "where": {
            "type": "string"
          },
          "generic": {
            "type": "boolean",
            "description": "The fallback code for an HTTP status."
          },
          "docs_url": {
            "type": "string",
            "format": "uri"
          }
        },
        "required": [
          "code",
          "status",
          "retryable",
          "summary",
          "action",
          "docs_url"
        ]
      },
      "PlanLimits": {
        "type": "object",
        "description": "A plan's limits, from the catalog enforcement reads. `apps` and\n`compute_hours_mo` may have no ceiling: then the value is null and\n`\u003ckey\u003e_unlimited` is true. Every other limit is a real number (0\ncustom domains means none).\n",
        "properties": {
          "apps": {
            "type": [
              "integer",
              "null"
            ]
          },
          "apps_unlimited": {
            "type": "boolean"
          },
          "apps_up_to": {
            "type": "integer",
            "description": "The number to show: the explicit cap, or what the RAM pool fits."
          },
          "compute_hours_mo": {
            "type": [
              "integer",
              "null"
            ]
          },
          "compute_hours_mo_unlimited": {
            "type": "boolean"
          },
          "pool_memory_mb": {
            "type": "integer"
          },
          "pool_volume_gb": {
            "type": "integer"
          },
          "bandwidth_gb_mo": {
            "type": "integer"
          },
          "cpus_per_app": {
            "type": "integer"
          },
          "cpu_kind": {
            "type": "string"
          },
          "custom_domains": {
            "type": "integer"
          },
          "sites": {
            "type": "integer"
          },
          "site_mb": {
            "type": "integer"
          },
          "autoscale_max": {
            "type": "integer"
          },
          "always_on_budget_cents": {
            "type": "integer",
            "description": "Most the account's always-on machines may cost a month at list price, as this server's always-on margin guard enforces it. 0 = no separate always-on budget (always on Free and Hobby, and on every plan while the guard only logs, the default)."
          },
          "always_on_performance": {
            "type": "boolean",
            "description": "Whether performance CPU may run always-on on this server. False on Pro and Team only while the guard enforces: performance CPU must then sleep when idle."
          }
        }
      },
      "PricingPlanCard": {
        "type": "object",
        "description": "One tier of one pricing version, as the switch shows it.",
        "properties": {
          "id": {
            "type": "string",
            "enum": [
              "free",
              "hobby",
              "pro",
              "team"
            ]
          },
          "name": {
            "type": "string",
            "description": "The plan's display name (`team` is Studio)"
          },
          "price_display": {
            "type": "string"
          },
          "limits": {
            "$ref": "#/components/schemas/PlanLimits"
          }
        },
        "required": [
          "id",
          "name",
          "price_display",
          "limits"
        ]
      },
      "SiteHold": {
        "type": "object",
        "description": "Names held with nothing published yet, against the cap.",
        "properties": {
          "used": {
            "type": "integer"
          },
          "limit": {
            "type": "integer"
          },
          "hours": {
            "type": "integer",
            "description": "How long an unfinished claim holds its name."
          }
        },
        "required": [
          "used",
          "limit",
          "hours"
        ]
      },
      "SiteDomain": {
        "type": "object",
        "properties": {
          "hostname": {
            "type": "string"
          },
          "is_custom": {
            "type": "boolean"
          },
          "tls_status": {
            "type": "string"
          }
        },
        "required": [
          "hostname",
          "is_custom",
          "tls_status"
        ]
      },
      "Site": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "mode": {
            "type": "string",
            "enum": [
              "website",
              "files"
            ]
          },
          "size_bytes": {
            "type": "integer"
          },
          "file_count": {
            "type": "integer"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "description": "Free plan only — when the site pauses."
          },
          "moderation": {
            "type": "object",
            "description": "The statement of reasons while the site is held or quarantined."
          },
          "domains": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "$ref": "#/components/schemas/SiteDomain"
            },
            "description": "Null when the list could not be read; ask GET /sites/{name}/domains."
          }
        },
        "required": [
          "name",
          "url",
          "mode",
          "size_bytes",
          "file_count",
          "updated_at",
          "domains"
        ]
      },
      "DNSProvider": {
        "type": "object",
        "description": "The DNS panel to open, detected from the zone's nameservers; url is empty when unrecognised.",
        "properties": {
          "key": {
            "type": "string",
            "example": "cloudflare"
          },
          "label": {
            "type": "string",
            "example": "Cloudflare"
          },
          "url": {
            "type": "string"
          }
        },
        "required": [
          "key",
          "label",
          "url"
        ]
      },
      "DomainRecords": {
        "type": "object",
        "description": "The DNS plan: a CNAME from the hostname to the base domain (an ALIAS\nwhere apex is true, since a bare domain cannot hold a CNAME) and the\nownership TXT at `_innstance-verify.\u003chostname\u003e`. No address appears.\n",
        "properties": {
          "cname": {
            "type": "object",
            "properties": {
              "name": {
                "type": "string",
                "example": "www.example.com"
              },
              "target": {
                "type": "string",
                "example": "innstance.app"
              },
              "apex": {
                "type": "boolean"
              }
            },
            "required": [
              "name",
              "target",
              "apex"
            ]
          },
          "verify": {
            "type": "object",
            "properties": {
              "name": {
                "type": "string",
                "example": "_innstance-verify.www.example.com"
              },
              "value": {
                "type": "string"
              }
            },
            "required": [
              "name",
              "value"
            ]
          }
        },
        "required": [
          "cname",
          "verify"
        ]
      },
      "AgentInput": {
        "type": "object",
        "description": "One input an assistant needs (a key, a token, a choice's field).",
        "properties": {
          "key": {
            "type": "string"
          },
          "group": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "help": {
            "type": "string"
          },
          "secret": {
            "type": "boolean",
            "description": "Mask it while typing; never echoed back."
          },
          "steps": {
            "type": "array",
            "items": {
              "type": "object"
            }
          },
          "connect": {
            "type": "string"
          },
          "connect_label": {
            "type": "string"
          }
        },
        "required": [
          "key",
          "title",
          "secret"
        ]
      },
      "AgentCatalogEntry": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "summary": {
            "type": "string"
          },
          "memory_mb": {
            "type": "integer",
            "description": "The machine this assistant needs, to warn before setup that it will not fit."
          },
          "version": {
            "type": "string"
          },
          "inputs": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AgentInput"
            }
          },
          "selections": {
            "type": "array",
            "items": {
              "type": "object"
            }
          },
          "extras": {
            "type": "object"
          }
        },
        "required": [
          "name",
          "title",
          "summary",
          "memory_mb",
          "version",
          "inputs"
        ]
      },
      "AgentHost": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "catalog": {
            "type": "string"
          },
          "version": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "description": "awaiting-secrets, launching, installing, verifying, reconfiguring, running, verify-failed or launch-failed"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "notes": {
            "type": "string",
            "description": "What the last setup found, in words."
          },
          "whatsapp_linkable": {
            "type": "boolean"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "description": "Free plan only — when it is destroyed."
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "The control panel's address."
          },
          "moderation": {
            "type": "object"
          },
          "panel_private": {
            "type": "boolean"
          },
          "dashboard_user": {
            "type": "string"
          },
          "dashboard_signin": {
            "type": "boolean"
          },
          "dashboard_password_env": {
            "type": "string",
            "description": "The setting's name, never the password."
          },
          "panel_password_shown": {
            "type": "boolean"
          },
          "panel_device_approval": {
            "type": "boolean"
          },
          "catalog_title": {
            "type": "string"
          },
          "ssh_user": {
            "type": "string",
            "description": "The account `agents ssh` lands in (once running)."
          }
        },
        "required": [
          "name",
          "catalog",
          "version",
          "status",
          "created_at"
        ]
      }
    }
  }
}
