{
  "components": {
    "schemas": {
      "Account": {
        "properties": {
          "allow_xlarge": {
            "type": "boolean"
          },
          "data_retention": {
            "$ref": "#/components/schemas/DataRetention",
            "default": {
              "monitoring_off": false,
              "snapshots_off": false,
              "zero_data_retention": false
            }
          },
          "email": {
            "type": "string"
          },
          "id": {
            "type": "string"
          },
          "max_sandboxes": {
            "format": "uint32",
            "minimum": 0.0,
            "type": "integer"
          }
        },
        "required": [
          "allow_xlarge",
          "email",
          "id",
          "max_sandboxes"
        ],
        "type": "object"
      },
      "AddSshKey": {
        "properties": {
          "name": {
            "type": "string"
          },
          "public_key": {
            "type": "string"
          }
        },
        "required": [
          "name",
          "public_key"
        ],
        "type": "object"
      },
      "ApiKeyInfo": {
        "description": "An API key as listed (the key itself is shown only once, at creation).",
        "properties": {
          "actions": {
            "default": [],
            "description": "What the key may do (`scopes::ACTIONS`); `[\"*\"]` = everything.",
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "created_at": {
            "format": "int64",
            "type": "integer"
          },
          "expires_at": {
            "default": null,
            "description": "None = never expires.",
            "format": "int64",
            "type": [
              "integer",
              "null"
            ]
          },
          "id": {
            "type": "string"
          },
          "label": {
            "type": "string"
          },
          "labels": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "Limited to sandboxes that have all of these labels; None = any labels.",
            "type": [
              "object",
              "null"
            ]
          },
          "last_used_at": {
            "default": null,
            "format": "int64",
            "type": [
              "integer",
              "null"
            ]
          },
          "preset": {
            "description": "The preset its actions match, if any (read-only, full-sandbox, ci, admin).",
            "type": [
              "string",
              "null"
            ]
          },
          "requests_30d": {
            "default": 0,
            "description": "Requests made with it in the last 30 days.",
            "format": "uint64",
            "minimum": 0.0,
            "type": "integer"
          },
          "sandboxes": {
            "default": null,
            "description": "The sandboxes it may touch (names), besides those it created; None = all.",
            "items": {
              "type": "string"
            },
            "type": [
              "array",
              "null"
            ]
          },
          "sandboxes_created": {
            "default": 0,
            "description": "Sandboxes this key created that still exist.",
            "format": "uint32",
            "minimum": 0.0,
            "type": "integer"
          }
        },
        "required": [
          "created_at",
          "id",
          "label"
        ],
        "type": "object"
      },
      "AuditEvent": {
        "description": "One entry of the account's audit log (`GET /audit`): who changed what, from where. Kept 90 days.",
        "properties": {
          "action": {
            "description": "What happened, e.g. sandbox.create, api_key.revoke, webhook.update, billing.plan.",
            "type": "string"
          },
          "actor": {
            "description": "Who did it: an API key id (`key_…`), `session` (the dashboard), or `public-url` (an auto-start by a visitor of a public URL).",
            "type": "string"
          },
          "actor_label": {
            "description": "The API key's name, or the account email for a dashboard session.",
            "type": [
              "string",
              "null"
            ]
          },
          "at": {
            "description": "Unix seconds.",
            "format": "int64",
            "type": "integer"
          },
          "detail": {
            "description": "Short extra facts, e.g. the sandboxes a create or fork made.",
            "type": [
              "string",
              "null"
            ]
          },
          "id": {
            "description": "`aud_…`",
            "type": "string"
          },
          "ip": {
            "description": "The client's IP address.",
            "type": [
              "string",
              "null"
            ]
          },
          "request_id": {
            "description": "The request's `X-Request-Id`.",
            "type": [
              "string",
              "null"
            ]
          },
          "target": {
            "description": "What it happened to: a sandbox, key, webhook or variable name or id (never a secret).",
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "action",
          "actor",
          "at",
          "id"
        ],
        "type": "object"
      },
      "AuditList": {
        "properties": {
          "events": {
            "description": "Newest first.",
            "items": {
              "$ref": "#/components/schemas/AuditEvent"
            },
            "type": "array"
          },
          "next_cursor": {
            "description": "Pass as `cursor` for the next (older) page; None at the end.",
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "events"
        ],
        "type": "object"
      },
      "AutoTopup": {
        "description": "`PUT /v1/billing/wallet/auto-topup`: charge this much whenever the plan's usage plus the wallet drops below 10% of it (at least $5). 0 turns it off.",
        "properties": {
          "amount_usd": {
            "format": "uint32",
            "minimum": 0.0,
            "type": "integer"
          }
        },
        "required": [
          "amount_usd"
        ],
        "type": "object"
      },
      "Billing": {
        "description": "Payment status of an account (`GET /v1/billing`).",
        "properties": {
          "auto_topup_failures": {
            "default": 0,
            "description": "Declined auto top-ups in a row (3 turn auto top-up off).",
            "format": "uint32",
            "minimum": 0.0,
            "type": "integer"
          },
          "auto_topup_usd": {
            "default": 0,
            "description": "Auto top-up amount in dollars; 0 = off.",
            "format": "uint32",
            "minimum": 0.0,
            "type": "integer"
          },
          "card_on_file": {
            "description": "A payment method is on file.",
            "type": "boolean"
          },
          "enabled": {
            "description": "Payments are set up on this froog server.",
            "type": "boolean"
          },
          "exempt": {
            "description": "The account isn't billed (e.g. the operator's own account).",
            "type": "boolean"
          },
          "grace_until": {
            "description": "Out of money: running sandboxes are stopped at this time (unix seconds) unless money is added.",
            "format": "int64",
            "type": [
              "integer",
              "null"
            ]
          },
          "included_usd": {
            "default": 0.0,
            "description": "This period: what the plan covers, what was used, and what's left (plan plus wallet).",
            "format": "double",
            "type": "number"
          },
          "max_wallet_per_day_usd": {
            "default": 0,
            "description": "Most the card can be charged for the wallet in 24 hours (payments and auto top-ups).",
            "format": "uint32",
            "minimum": 0.0,
            "type": "integer"
          },
          "min_topup_usd": {
            "default": 0,
            "description": "Smallest wallet payment, dollars.",
            "format": "uint32",
            "minimum": 0.0,
            "type": "integer"
          },
          "plan": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/CurrentPlan"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "The account's plan; None until one is chosen."
          },
          "plans": {
            "default": [],
            "description": "The plans on offer, cheapest first.",
            "items": {
              "$ref": "#/components/schemas/PlanInfo"
            },
            "type": "array"
          },
          "rates": {
            "default": [],
            "description": "Per-second rates, quoted per hour and per 730-hour month.",
            "items": {
              "$ref": "#/components/schemas/Rate"
            },
            "type": "array"
          },
          "remaining_usd": {
            "default": 0.0,
            "format": "double",
            "type": "number"
          },
          "suspended": {
            "description": "Out of money: the plan's usage and the wallet ran out and the grace period ended, so sandboxes were stopped. Add money to start them again.",
            "type": "boolean"
          },
          "used_usd": {
            "default": 0.0,
            "format": "double",
            "type": "number"
          },
          "wallet_usd": {
            "default": 0.0,
            "description": "The wallet: money paid in minus usage past the plan. Negative = owed.",
            "format": "double",
            "type": "number"
          }
        },
        "required": [
          "card_on_file",
          "enabled",
          "exempt",
          "suspended"
        ],
        "type": "object"
      },
      "BillingUrl": {
        "description": "A Stripe-hosted page to open in a browser.",
        "properties": {
          "url": {
            "type": "string"
          }
        },
        "required": [
          "url"
        ],
        "type": "object"
      },
      "BrowserRequest": {
        "description": "`POST /sandboxes/{name}/browser`: open the browser view (only Chrome).",
        "properties": {
          "profile": {
            "description": "Chrome's user data directory on the sandbox: an absolute path outside the system directories, created if missing. Default: `~/.config/froog-browser`.",
            "type": [
              "string",
              "null"
            ]
          }
        },
        "type": "object"
      },
      "ChoosePlan": {
        "description": "`POST /v1/billing/plan`: choose or change the plan.",
        "properties": {
          "plan": {
            "description": "Dollars a month: one of `Billing.plans`.",
            "format": "uint32",
            "minimum": 0.0,
            "type": "integer"
          }
        },
        "required": [
          "plan"
        ],
        "type": "object"
      },
      "CliLoginInfo": {
        "description": "`GET /cli-login/{code}`: what's being approved.",
        "properties": {
          "code": {
            "type": "string"
          },
          "device": {
            "type": "string"
          },
          "expires_at": {
            "format": "int64",
            "type": "integer"
          }
        },
        "required": [
          "code",
          "device",
          "expires_at"
        ],
        "type": "object"
      },
      "CliLoginPoll": {
        "description": "`POST /cli-login/poll`",
        "properties": {
          "poll_token": {
            "type": "string"
          }
        },
        "required": [
          "poll_token"
        ],
        "type": "object"
      },
      "CliLoginRequest": {
        "description": "`POST /cli-login` (no auth): start an `froog login` in the browser.",
        "properties": {
          "device": {
            "default": "",
            "description": "Shown on the approval page, e.g. the computer's hostname.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "CliLoginResult": {
        "properties": {
          "email": {
            "type": [
              "string",
              "null"
            ]
          },
          "key": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "description": "\"pending\" or \"approved\"",
            "type": "string"
          }
        },
        "required": [
          "status"
        ],
        "type": "object"
      },
      "CliLoginStarted": {
        "properties": {
          "code": {
            "description": "Show this; the dashboard asks the person to check it matches.",
            "type": "string"
          },
          "expires_in": {
            "format": "int64",
            "type": "integer"
          },
          "poll_token": {
            "description": "Secret: poll with it. Never show it.",
            "type": "string"
          },
          "url": {
            "description": "Open this in the browser.",
            "type": "string"
          }
        },
        "required": [
          "code",
          "expires_in",
          "poll_token",
          "url"
        ],
        "type": "object"
      },
      "CommandList": {
        "properties": {
          "commands": {
            "items": {
              "$ref": "#/components/schemas/CommandRecord"
            },
            "type": "array"
          },
          "next_cursor": {
            "description": "Pass as `cursor` for the next (older) page; None at the end.",
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "commands"
        ],
        "type": "object"
      },
      "CommandLogs": {
        "description": "A chunk of a command's stored output from `offset`. Read again from `next_offset` until `done` (the command ended and everything was read).",
        "properties": {
          "data": {
            "description": "UTF-8 text, or base64 with `encoding=base64`.",
            "type": "string"
          },
          "done": {
            "type": "boolean"
          },
          "next_offset": {
            "format": "uint64",
            "minimum": 0.0,
            "type": "integer"
          },
          "offset": {
            "format": "uint64",
            "minimum": 0.0,
            "type": "integer"
          },
          "size": {
            "description": "Bytes stored so far for this stream.",
            "format": "uint64",
            "minimum": 0.0,
            "type": "integer"
          },
          "status": {
            "type": "string"
          }
        },
        "required": [
          "data",
          "done",
          "next_offset",
          "offset",
          "size",
          "status"
        ],
        "type": "object"
      },
      "CommandRecord": {
        "description": "One command in a sandbox's history (`GET /sandboxes/{name}/command-history`). Kept 90 days, readable while the sandbox is stopped. `mode`: sync | stream | detached. `status`: running | paused (frozen with a sandbox stopped with its memory kept; a start resumes it) | exited | timed_out | failed | killed | lost.",
        "properties": {
          "command": {
            "description": "The command text (first 64 KiB).",
            "type": "string"
          },
          "ended_at": {
            "format": "int64",
            "type": [
              "integer",
              "null"
            ]
          },
          "exit_code": {
            "format": "int32",
            "type": [
              "integer",
              "null"
            ]
          },
          "id": {
            "type": "string"
          },
          "job_id": {
            "description": "The background job behind a stream/detached command (`froog logs`, `froog kill`).",
            "type": [
              "string",
              "null"
            ]
          },
          "mode": {
            "type": "string"
          },
          "output_truncated": {
            "description": "Output past the per-command or per-account cap was dropped.",
            "type": "boolean"
          },
          "started_at": {
            "format": "int64",
            "type": "integer"
          },
          "status": {
            "type": "string"
          },
          "stderr_size": {
            "format": "uint64",
            "minimum": 0.0,
            "type": "integer"
          },
          "stdout_size": {
            "format": "uint64",
            "minimum": 0.0,
            "type": "integer"
          }
        },
        "required": [
          "command",
          "id",
          "mode",
          "output_truncated",
          "started_at",
          "status",
          "stderr_size",
          "stdout_size"
        ],
        "type": "object"
      },
      "CreateSandbox": {
        "properties": {
          "auto_start": {
            "description": "true: while stopped, a request to one of its public URLs, or an exec, file, job or wait call, starts it. Default false.",
            "type": [
              "boolean",
              "null"
            ]
          },
          "count": {
            "description": "Create `count` sandboxes named `name-1..name-N`.",
            "format": "uint32",
            "minimum": 0.0,
            "type": [
              "integer",
              "null"
            ]
          },
          "delete_after_secs": {
            "description": "Delete it after it has stayed stopped this many seconds (0 or absent = never; at most 365 days). A start before then cancels the countdown.",
            "format": "uint64",
            "minimum": 0.0,
            "type": [
              "integer",
              "null"
            ]
          },
          "env": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "Its own environment variables (on top of the account's; they win, and apply even with `environment: false`). With `from`, the default is the saved snapshot's source's.",
            "type": [
              "object",
              "null"
            ]
          },
          "environment": {
            "description": "false: start with none of the account's environment (no env vars, repos, files or setup script), e.g. for code or people you don't trust with your credentials.",
            "type": [
              "boolean",
              "null"
            ]
          },
          "files": {
            "description": "Files written into the home directory before the sandbox counts as running (first boot only; mode 600; at most 20, 1 MiB together).",
            "items": {
              "$ref": "#/components/schemas/NewFile"
            },
            "type": [
              "array",
              "null"
            ]
          },
          "from": {
            "description": "Start from this saved snapshot (its name): its disk, OS and, unless `size` is given, size.",
            "type": [
              "string",
              "null"
            ]
          },
          "labels": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "Key-value tags (at most 32; keys 1-63 of `a-z A-Z 0-9 . _ - /`, values up to 255 characters).",
            "type": [
              "object",
              "null"
            ]
          },
          "max_lifetime_secs": {
            "description": "Stop it this many seconds after each start, busy or not, even while an agent prompt runs (0 or absent = no limit; at most 30 days). The auto-stop timer (`ttl_secs`) still applies.",
            "format": "uint64",
            "minimum": 0.0,
            "type": [
              "integer",
              "null"
            ]
          },
          "monitoring": {
            "description": "false: keep no command history or process samples. Default: the account's setting.",
            "type": [
              "boolean",
              "null"
            ]
          },
          "name": {
            "type": [
              "string",
              "null"
            ]
          },
          "os": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Os"
              },
              {
                "type": "null"
              }
            ],
            "description": "Operating system; default Ubuntu 24.04."
          },
          "repo": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/SandboxRepo"
              },
              {
                "type": "null"
              }
            ],
            "description": "A git repo cloned into this sandbox only, by the setup job at boot (also with `environment: false`)."
          },
          "setup": {
            "description": "A script run once, in the background, after the first boot (as job \"setup\").",
            "type": [
              "string",
              "null"
            ]
          },
          "size": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Size"
              },
              {
                "type": "null"
              }
            ]
          },
          "snapshots": {
            "description": "false: never snapshot it; a stop erases it. Default: the account's setting.",
            "type": [
              "boolean",
              "null"
            ]
          },
          "ttl_secs": {
            "description": "Auto-stop this many seconds after each start, busy or not (at most 30 days; 0 = no auto-stop). Absent or null: 1 hour (2 hours during the free trial, which needs one).",
            "format": "uint64",
            "minimum": 0.0,
            "type": [
              "integer",
              "null"
            ]
          }
        },
        "type": "object"
      },
      "CreateSnapshot": {
        "properties": {
          "label": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "type": "object"
      },
      "CreatedApiKey": {
        "properties": {
          "actions": {
            "default": [],
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "expires_at": {
            "default": null,
            "format": "int64",
            "type": [
              "integer",
              "null"
            ]
          },
          "id": {
            "type": "string"
          },
          "key": {
            "description": "Shown once; store it now.",
            "type": "string"
          },
          "label": {
            "type": "string"
          },
          "labels": {
            "additionalProperties": {
              "type": "string"
            },
            "type": [
              "object",
              "null"
            ]
          },
          "sandboxes": {
            "default": null,
            "items": {
              "type": "string"
            },
            "type": [
              "array",
              "null"
            ]
          }
        },
        "required": [
          "id",
          "key",
          "label"
        ],
        "type": "object"
      },
      "CurrentPlan": {
        "description": "The account's plan, mirrored from its Stripe subscription.",
        "properties": {
          "cancel_at_period_end": {
            "description": "Canceled: the plan ends with the period.",
            "type": "boolean"
          },
          "max_sandboxes": {
            "format": "uint32",
            "minimum": 0.0,
            "type": "integer"
          },
          "period_end": {
            "format": "int64",
            "type": "integer"
          },
          "period_start": {
            "description": "The current billing period (or the trial), unix seconds.",
            "format": "int64",
            "type": "integer"
          },
          "saved_snapshots": {
            "format": "uint32",
            "minimum": 0.0,
            "type": "integer"
          },
          "status": {
            "description": "trialing | active | past_due | unpaid | canceled | incomplete",
            "type": "string"
          },
          "trial_ends_at": {
            "format": "int64",
            "type": [
              "integer",
              "null"
            ]
          },
          "usd": {
            "description": "Dollars a month.",
            "format": "uint32",
            "minimum": 0.0,
            "type": "integer"
          }
        },
        "required": [
          "cancel_at_period_end",
          "max_sandboxes",
          "period_end",
          "period_start",
          "saved_snapshots",
          "status",
          "usd"
        ],
        "type": "object"
      },
      "DataRetention": {
        "description": "`GET/PATCH /account/data-retention`. Changing it needs a dashboard session, not an API key.",
        "properties": {
          "monitoring_off": {
            "description": "New sandboxes keep no command history or process samples.",
            "type": "boolean"
          },
          "snapshots_off": {
            "description": "New sandboxes are never snapshotted (a stop erases them).",
            "type": "boolean"
          },
          "zero_data_retention": {
            "description": "Erase each sandbox's data (disk, snapshots, history) when it stops; no saved snapshots.",
            "type": "boolean"
          }
        },
        "required": [
          "monitoring_off",
          "snapshots_off",
          "zero_data_retention"
        ],
        "type": "object"
      },
      "DeletedSnapshots": {
        "description": "`DELETE /sandboxes/{name}/snapshots`: the automatic snapshots that were deleted.",
        "properties": {
          "deleted": {
            "description": "Their ids.",
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "kept": {
            "description": "The snapshot a stopped (or errored) sandbox will start from, which is kept.",
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "deleted"
        ],
        "type": "object"
      },
      "DesktopLink": {
        "description": "`POST /sandboxes/{name}/desktop`: a link to the VM's desktop in the browser.",
        "properties": {
          "expires_at": {
            "format": "int64",
            "type": "integer"
          },
          "url": {
            "description": "Secret: carries a token (in the fragment, after `#`). Open it within 10 minutes; an open session keeps working. Add `overlay=0` to the query (before the `#`) to hide the viewer's controls and stats, e.g. in an iframe.",
            "type": "string"
          }
        },
        "required": [
          "expires_at",
          "url"
        ],
        "type": "object"
      },
      "EnvFile": {
        "description": "A secret file written into every sandbox at each start (content never returned).",
        "properties": {
          "path": {
            "description": "Relative to the home directory, e.g. `.npmrc` or `app/.env`.",
            "type": "string"
          },
          "size": {
            "format": "uint64",
            "minimum": 0.0,
            "type": "integer"
          },
          "updated_at": {
            "format": "int64",
            "type": "integer"
          }
        },
        "required": [
          "path",
          "size",
          "updated_at"
        ],
        "type": "object"
      },
      "EnvVar": {
        "description": "An account environment variable as listed (the value is never returned, only a preview).",
        "properties": {
          "name": {
            "type": "string"
          },
          "preview": {
            "description": "e.g. \"sk-a…9xQ\"; the whole value if it's short and not secret-looking.",
            "type": "string"
          },
          "updated_at": {
            "format": "int64",
            "type": "integer"
          }
        },
        "required": [
          "name",
          "preview",
          "updated_at"
        ],
        "type": "object"
      },
      "Environment": {
        "description": "The account's environment: what every new sandbox starts with, besides env vars.",
        "properties": {
          "files": {
            "items": {
              "$ref": "#/components/schemas/EnvFile"
            },
            "type": "array"
          },
          "repos": {
            "description": "Git URLs cloned into `~/<repo>` when the directory doesn't exist yet (`https://github.com/org/repo`, optionally `#branch`).",
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "setup": {
            "description": "Shell script run in the background at every start, after cloning (as job \"setup\").",
            "type": [
              "string",
              "null"
            ]
          },
          "updated_at": {
            "format": "int64",
            "type": "integer"
          }
        },
        "required": [
          "files",
          "repos",
          "updated_at"
        ],
        "type": "object"
      },
      "ErrorBody": {
        "properties": {
          "error": {
            "$ref": "#/components/schemas/ErrorDetail"
          }
        },
        "required": [
          "error"
        ],
        "type": "object"
      },
      "ErrorDetail": {
        "properties": {
          "code": {
            "type": "string"
          },
          "message": {
            "type": "string"
          },
          "request_id": {
            "description": "This request's id (also in the `X-Request-Id` header); quote it when reporting a problem.",
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "code",
          "message"
        ],
        "type": "object"
      },
      "ExecEvent": {
        "description": "One line of a streamed exec (`stream: true`): `started` first, then `stdout`/`stderr` chunks as they arrive, then exactly one `exit` or `error`.",
        "oneOf": [
          {
            "properties": {
              "command_id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "type": {
                "enum": [
                  "started"
                ],
                "type": "string"
              }
            },
            "required": [
              "type"
            ],
            "type": "object"
          },
          {
            "description": "A UTF-8 chunk of output (invalid bytes become U+FFFD).",
            "properties": {
              "data": {
                "type": "string"
              },
              "type": {
                "enum": [
                  "stdout"
                ],
                "type": "string"
              }
            },
            "required": [
              "data",
              "type"
            ],
            "type": "object"
          },
          {
            "properties": {
              "data": {
                "type": "string"
              },
              "type": {
                "enum": [
                  "stderr"
                ],
                "type": "string"
              }
            },
            "required": [
              "data",
              "type"
            ],
            "type": "object"
          },
          {
            "description": "124 when it timed out.",
            "properties": {
              "exit_code": {
                "format": "int32",
                "type": "integer"
              },
              "timed_out": {
                "type": "boolean"
              },
              "type": {
                "enum": [
                  "exit"
                ],
                "type": "string"
              }
            },
            "required": [
              "exit_code",
              "timed_out",
              "type"
            ],
            "type": "object"
          },
          {
            "description": "The command couldn't finish: the connection to the sandbox broke, or froog failed.",
            "properties": {
              "code": {
                "type": "string"
              },
              "message": {
                "type": "string"
              },
              "type": {
                "enum": [
                  "error"
                ],
                "type": "string"
              }
            },
            "required": [
              "code",
              "message",
              "type"
            ],
            "type": "object"
          }
        ]
      },
      "ExecRequest": {
        "properties": {
          "command": {
            "type": "string"
          },
          "cwd": {
            "description": "Run in this directory (relative to the home directory, or absolute).",
            "type": [
              "string",
              "null"
            ]
          },
          "env": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "Extra environment variables for this command only.",
            "type": [
              "object",
              "null"
            ]
          },
          "stdin": {
            "description": "Text fed to the command's standard input, which then ends (at most 1 MiB). Without it (and `stdin_b64`), stdin is empty.",
            "type": [
              "string",
              "null"
            ]
          },
          "stdin_b64": {
            "description": "Like `stdin`, base64-encoded (for binary input; at most 1 MiB decoded).",
            "type": [
              "string",
              "null"
            ]
          },
          "stream": {
            "description": "true: answer with `application/x-ndjson`, one `ExecEvent` per line as output arrives (also chosen by `Accept: application/x-ndjson`).",
            "type": [
              "boolean",
              "null"
            ]
          },
          "timeout_secs": {
            "format": "uint64",
            "minimum": 0.0,
            "type": [
              "integer",
              "null"
            ]
          }
        },
        "required": [
          "command"
        ],
        "type": "object"
      },
      "ExecResult": {
        "properties": {
          "command_id": {
            "description": "The command's record in the history (`GET /sandboxes/{name}/command-history/{id}`).",
            "type": [
              "string",
              "null"
            ]
          },
          "exit_code": {
            "description": "124 when it timed out.",
            "format": "int32",
            "type": "integer"
          },
          "stderr": {
            "type": "string"
          },
          "stdout": {
            "type": "string"
          },
          "timed_out": {
            "default": false,
            "description": "It ran past `timeout_secs` and was killed; stdout and stderr hold what it printed until then.",
            "type": "boolean"
          }
        },
        "required": [
          "exit_code",
          "stderr",
          "stdout"
        ],
        "type": "object"
      },
      "Feedback": {
        "description": "`POST /feedback`: a message to the froog team (replies go to the account email).",
        "properties": {
          "message": {
            "type": "string"
          }
        },
        "required": [
          "message"
        ],
        "type": "object"
      },
      "FileEntry": {
        "description": "One entry of a directory listing. `kind`: file | dir | link | other.",
        "properties": {
          "kind": {
            "type": "string"
          },
          "modified": {
            "format": "int64",
            "type": "integer"
          },
          "name": {
            "type": "string"
          },
          "size": {
            "format": "uint64",
            "minimum": 0.0,
            "type": "integer"
          }
        },
        "required": [
          "kind",
          "modified",
          "name",
          "size"
        ],
        "type": "object"
      },
      "FileFind": {
        "description": "`GET /sandboxes/{name}/files/find?path=&pattern=&regex=`: lines that contain a text.",
        "properties": {
          "errors": {
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "matches": {
            "description": "At most 1,000.",
            "items": {
              "$ref": "#/components/schemas/FileMatch"
            },
            "type": "array"
          },
          "truncated": {
            "type": "boolean"
          }
        },
        "required": [
          "matches",
          "truncated"
        ],
        "type": "object"
      },
      "FileInfo": {
        "description": "`GET /sandboxes/{name}/files?path=`: a file's details, or a directory and its entries.",
        "properties": {
          "entries": {
            "items": {
              "$ref": "#/components/schemas/FileEntry"
            },
            "type": [
              "array",
              "null"
            ]
          },
          "kind": {
            "type": "string"
          },
          "modified": {
            "format": "int64",
            "type": "integer"
          },
          "path": {
            "type": "string"
          },
          "size": {
            "format": "uint64",
            "minimum": 0.0,
            "type": "integer"
          }
        },
        "required": [
          "kind",
          "modified",
          "path",
          "size"
        ],
        "type": "object"
      },
      "FileMatch": {
        "properties": {
          "content": {
            "description": "The line, cut at 500 characters.",
            "type": "string"
          },
          "file": {
            "type": "string"
          },
          "line": {
            "format": "uint64",
            "minimum": 0.0,
            "type": "integer"
          }
        },
        "required": [
          "content",
          "file",
          "line"
        ],
        "type": "object"
      },
      "FilePermissions": {
        "description": "`POST /sandboxes/{name}/files/permissions`: chmod. The owner (the sandbox user) stays.",
        "properties": {
          "mode": {
            "description": "Octal, e.g. 755 or 0600.",
            "type": "string"
          },
          "path": {
            "type": "string"
          },
          "recursive": {
            "default": false,
            "description": "Also everything inside a directory.",
            "type": "boolean"
          }
        },
        "required": [
          "mode",
          "path"
        ],
        "type": "object"
      },
      "FileReplace": {
        "description": "`POST /sandboxes/{name}/files/replace`: replace every occurrence of `pattern` (plain text, byte for byte) with `new_value` in each file (up to 100 files, 5 MiB each).",
        "properties": {
          "new_value": {
            "type": "string"
          },
          "paths": {
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "pattern": {
            "type": "string"
          }
        },
        "required": [
          "new_value",
          "paths",
          "pattern"
        ],
        "type": "object"
      },
      "FileReplaced": {
        "properties": {
          "error": {
            "type": [
              "string",
              "null"
            ]
          },
          "file": {
            "type": "string"
          },
          "replacements": {
            "format": "uint64",
            "minimum": 0.0,
            "type": [
              "integer",
              "null"
            ]
          },
          "success": {
            "type": "boolean"
          }
        },
        "required": [
          "file",
          "success"
        ],
        "type": "object"
      },
      "FileSearch": {
        "description": "`GET /sandboxes/{name}/files/search?path=&pattern=`: paths whose name matches a glob.",
        "properties": {
          "errors": {
            "description": "Parts of the tree that couldn't be searched (the results are then incomplete).",
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "files": {
            "description": "Absolute paths, at most 10,000.",
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "truncated": {
            "type": "boolean"
          }
        },
        "required": [
          "files",
          "truncated"
        ],
        "type": "object"
      },
      "FileUrl": {
        "description": "A URL that GETs (download) or PUTs (upload, raw bytes) one file with no Authorization header.",
        "properties": {
          "expires_at": {
            "format": "int64",
            "type": "integer"
          },
          "method": {
            "type": "string"
          },
          "url": {
            "type": "string"
          }
        },
        "required": [
          "expires_at",
          "method",
          "url"
        ],
        "type": "object"
      },
      "ForkSandbox": {
        "properties": {
          "count": {
            "format": "uint32",
            "minimum": 0.0,
            "type": [
              "integer",
              "null"
            ]
          },
          "env": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "Environment variables set on top of the source's own (these win).",
            "type": [
              "object",
              "null"
            ]
          },
          "labels": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "Labels set on top of the source's own (these win).",
            "type": [
              "object",
              "null"
            ]
          },
          "monitoring": {
            "description": "Override the source's monitoring setting.",
            "type": [
              "boolean",
              "null"
            ]
          },
          "name": {
            "type": "string"
          },
          "size": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Size"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "Size of the new sandboxes; defaults to the source's."
          },
          "snapshot": {
            "description": "Snapshot id or label. Defaults to a fresh snapshot (running) or the latest one (stopped).",
            "type": [
              "string",
              "null"
            ]
          },
          "snapshots": {
            "description": "false: never snapshot the copies; a stop erases them. Default: the account's setting.",
            "type": [
              "boolean",
              "null"
            ]
          },
          "ttl_secs": {
            "description": "Auto-stop timer of the copies in seconds (0 = no auto-stop). Default: the source's.",
            "format": "uint64",
            "minimum": 0.0,
            "type": [
              "integer",
              "null"
            ]
          }
        },
        "required": [
          "name"
        ],
        "type": "object"
      },
      "Job": {
        "description": "A background command. `state`: running | paused (frozen with a sandbox stopped with its memory kept; a start resumes it) | exited | killed | lost (the sandbox stopped under it).",
        "properties": {
          "command": {
            "description": "First 200 characters.",
            "type": "string"
          },
          "command_id": {
            "description": "The job's record in the command history (None for jobs froog started itself, like setup).",
            "type": [
              "string",
              "null"
            ]
          },
          "exit_code": {
            "format": "int32",
            "type": [
              "integer",
              "null"
            ]
          },
          "id": {
            "type": "string"
          },
          "started_at": {
            "format": "int64",
            "type": "integer"
          },
          "state": {
            "type": "string"
          }
        },
        "required": [
          "command",
          "id",
          "started_at",
          "state"
        ],
        "type": "object"
      },
      "JobOutput": {
        "description": "A job's state and a chunk of its output (stdout and stderr together), read from `offset`. Pass `next_offset` as the next `offset` to follow it.",
        "properties": {
          "exit_code": {
            "format": "int32",
            "type": [
              "integer",
              "null"
            ]
          },
          "id": {
            "type": "string"
          },
          "next_offset": {
            "format": "uint64",
            "minimum": 0.0,
            "type": "integer"
          },
          "output": {
            "type": "string"
          },
          "output_size": {
            "format": "uint64",
            "minimum": 0.0,
            "type": "integer"
          },
          "started_at": {
            "format": "int64",
            "type": "integer"
          },
          "state": {
            "type": "string"
          }
        },
        "required": [
          "id",
          "next_offset",
          "output",
          "output_size",
          "started_at",
          "state"
        ],
        "type": "object"
      },
      "JsonRpcError": {
        "properties": {
          "code": {
            "description": "-32700 parse error, -32600 invalid request, -32601 unknown method, -32602 bad params.",
            "format": "int64",
            "type": "integer"
          },
          "message": {
            "type": "string"
          }
        },
        "required": [
          "code",
          "message"
        ],
        "type": "object"
      },
      "JsonRpcRequest": {
        "description": "A JSON-RPC 2.0 message to `POST /mcp` (a batch is an array of them). Without `id` it's a notification, answered with 202 and no body.",
        "properties": {
          "id": true,
          "jsonrpc": {
            "description": "Always \"2.0\".",
            "type": "string"
          },
          "method": {
            "description": "initialize, ping, tools/list, tools/call, notifications/initialized, …",
            "type": "string"
          },
          "params": true
        },
        "required": [
          "jsonrpc",
          "method"
        ],
        "type": "object"
      },
      "JsonRpcResponse": {
        "description": "The answer to a JSON-RPC request: `result` or `error`. A failed tool call is a `result` with `isError: true` and the API error (`code`, `message`, `request_id`) as its text.",
        "properties": {
          "error": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/JsonRpcError"
              },
              {
                "type": "null"
              }
            ]
          },
          "id": true,
          "jsonrpc": {
            "type": "string"
          },
          "result": true
        },
        "required": [
          "id",
          "jsonrpc"
        ],
        "type": "object"
      },
      "Limits": {
        "description": "What an account may do now: `GET /v1/limits`.",
        "properties": {
          "max_sandboxes": {
            "description": "Sandboxes at once (lower during the free trial).",
            "format": "uint32",
            "minimum": 0.0,
            "type": "integer"
          },
          "running": {
            "description": "Running or starting now.",
            "format": "uint32",
            "minimum": 0.0,
            "type": "integer"
          },
          "saved_snapshot_allowance": {
            "$ref": "#/components/schemas/SavedSnapshotAllowance",
            "default": {
              "extra": 0,
              "included": 0,
              "price_per_extra_day_usd": 0.0,
              "used": 0
            },
            "description": "Saved snapshots included in the plan vs kept now."
          },
          "sizes": {
            "description": "Sizes the account can start.",
            "items": {
              "$ref": "#/components/schemas/Size"
            },
            "type": "array"
          },
          "starts": {
            "$ref": "#/components/schemas/StartLimits"
          },
          "trial": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Trial"
              },
              {
                "type": "null"
              }
            ],
            "description": "The free trial; None when this account has none (billing off, or exempt)."
          }
        },
        "required": [
          "max_sandboxes",
          "running",
          "sizes",
          "starts"
        ],
        "type": "object"
      },
      "MakeDir": {
        "properties": {
          "mode": {
            "description": "Octal permissions of the new directory, e.g. 700 (default: the umask's, usually 755).",
            "type": [
              "string",
              "null"
            ]
          },
          "path": {
            "type": "string"
          }
        },
        "required": [
          "path"
        ],
        "type": "object"
      },
      "MoveFile": {
        "properties": {
          "from": {
            "type": "string"
          },
          "to": {
            "type": "string"
          }
        },
        "required": [
          "from",
          "to"
        ],
        "type": "object"
      },
      "NewApiKey": {
        "properties": {
          "actions": {
            "items": {
              "type": "string"
            },
            "type": [
              "array",
              "null"
            ]
          },
          "label": {
            "type": [
              "string",
              "null"
            ]
          },
          "labels": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "Limit the key to sandboxes that have all of these labels. Sandboxes it creates or forks get them, and it can't change or remove them.",
            "type": [
              "object",
              "null"
            ]
          },
          "never_expires": {
            "default": false,
            "description": "A key that works until revoked.",
            "type": "boolean"
          },
          "preset": {
            "description": "read-only | full-sandbox | ci | admin (`scopes::PRESETS`). Use this or `actions`.",
            "type": [
              "string",
              "null"
            ]
          },
          "sandboxes": {
            "description": "Limit the key to these sandboxes (names), plus any it creates.",
            "items": {
              "type": "string"
            },
            "type": [
              "array",
              "null"
            ]
          },
          "ttl_days": {
            "default": null,
            "description": "Days until it expires (1-365). Default 90.",
            "format": "uint32",
            "minimum": 0.0,
            "type": [
              "integer",
              "null"
            ]
          }
        },
        "type": "object"
      },
      "NewFile": {
        "description": "A file to write into a new sandbox (`CreateSandbox.files`).",
        "properties": {
          "content": {
            "type": "string"
          },
          "encoding": {
            "description": "utf8 (default) or base64 (for binary files).",
            "type": [
              "string",
              "null"
            ]
          },
          "path": {
            "description": "Relative to the home directory, e.g. `app/.env`.",
            "type": "string"
          }
        },
        "required": [
          "content",
          "path"
        ],
        "type": "object"
      },
      "NewFileUrl": {
        "description": "`POST /sandboxes/{name}/files/download-url` or `/upload-url`.",
        "properties": {
          "expires_in": {
            "description": "Seconds the URL works (60-86400, default 900).",
            "format": "uint64",
            "minimum": 0.0,
            "type": [
              "integer",
              "null"
            ]
          },
          "path": {
            "type": "string"
          }
        },
        "required": [
          "path"
        ],
        "type": "object"
      },
      "NewJob": {
        "description": "Start a background command: `POST /sandboxes/{name}/jobs`.",
        "properties": {
          "command": {
            "type": "string"
          },
          "cwd": {
            "description": "Run in this directory (relative to the home directory, or absolute).",
            "type": [
              "string",
              "null"
            ]
          },
          "env": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "Extra environment variables for this command only.",
            "type": [
              "object",
              "null"
            ]
          },
          "mode": {
            "description": "\"detached\" (default) or \"stream\" (`froog exec --timeout 0` follows it); shown in the history.",
            "type": [
              "string",
              "null"
            ]
          },
          "stdin": {
            "description": "Text for the job's standard input, given at start (at most 1 MiB). Kept with the job on the sandbox's disk (`~/.froog/jobs/<id>/stdin`).",
            "type": [
              "string",
              "null"
            ]
          },
          "stdin_b64": {
            "description": "Like `stdin`, base64-encoded (at most 1 MiB decoded).",
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "command"
        ],
        "type": "object"
      },
      "NewPublicUrl": {
        "description": "`POST /sandboxes/{name}/urls`: host a port, or change how an already hosted port is reached.",
        "properties": {
          "port": {
            "format": "uint16",
            "minimum": 0.0,
            "type": "integer"
          },
          "public": {
            "default": false,
            "description": "true: no token. Default false (private), like boat. Hosting a public port again without it gives it a new token; with it, removes the token.",
            "type": "boolean"
          },
          "subdomain": {
            "description": "Rename the URL to `https://<subdomain>.<domain>` (3-40 lowercase letters, digits, hyphens).",
            "type": [
              "string",
              "null"
            ]
          },
          "title": {
            "description": "A label for people (at most 100 characters); \"\" removes it. Hosting again without it keeps the old one.",
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "port"
        ],
        "type": "object"
      },
      "NewUrlToken": {
        "description": "`POST /sandboxes/{name}/urls/{port}/tokens`: another token for a private URL, e.g. one per viewer, that expires and can be revoked on its own.",
        "properties": {
          "label": {
            "description": "Who or what it's for (at most 100 characters).",
            "type": [
              "string",
              "null"
            ]
          },
          "ttl_secs": {
            "description": "Seconds until it stops working: 60 to 30 days, default 1 day.",
            "format": "uint64",
            "minimum": 0.0,
            "type": [
              "integer",
              "null"
            ]
          }
        },
        "type": "object"
      },
      "NewWebhook": {
        "properties": {
          "events": {
            "default": [],
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "name": {
            "type": [
              "string",
              "null"
            ]
          },
          "url": {
            "type": "string"
          }
        },
        "required": [
          "url"
        ],
        "type": "object"
      },
      "OpenPort": {
        "description": "`POST /sandboxes/{name}/ports`: open one inbound port on the sandbox's IPv6 address.",
        "properties": {
          "port": {
            "format": "uint16",
            "minimum": 0.0,
            "type": "integer"
          },
          "protocol": {
            "default": "tcp",
            "description": "tcp (default) or udp.",
            "type": "string"
          }
        },
        "required": [
          "port"
        ],
        "type": "object"
      },
      "Os": {
        "description": "The operating system a sandbox runs. All come with the same tools. `omarchy` is Arch Linux with Omarchy's Hyprland desktop (a sandbox with a screen). Accepts the id or the distro's name (\"debian\").",
        "enum": [
          "ubuntu-24.04",
          "ubuntu-26.04",
          "debian-13",
          "omarchy"
        ],
        "type": "string"
      },
      "OsImage": {
        "description": "An OS sandboxes can start with: `GET /v1/images`.",
        "properties": {
          "default": {
            "type": "boolean"
          },
          "id": {
            "$ref": "#/components/schemas/Os"
          },
          "name": {
            "type": "string"
          }
        },
        "required": [
          "default",
          "id",
          "name"
        ],
        "type": "object"
      },
      "PlanChange": {
        "description": "What choosing a plan did: a Checkout page to open (first plan), or the plan changed.",
        "properties": {
          "billing": {
            "$ref": "#/components/schemas/Billing"
          },
          "url": {
            "description": "Open this Stripe page to subscribe (and save a card). None = changed already.",
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "billing"
        ],
        "type": "object"
      },
      "PlanInfo": {
        "description": "A plan on offer.",
        "properties": {
          "max_sandboxes": {
            "description": "Sandboxes at once.",
            "format": "uint32",
            "minimum": 0.0,
            "type": "integer"
          },
          "saved_snapshots": {
            "description": "Saved snapshots included.",
            "format": "uint32",
            "minimum": 0.0,
            "type": "integer"
          },
          "usd": {
            "description": "Dollars a month, all of it usable as running time and storage.",
            "format": "uint32",
            "minimum": 0.0,
            "type": "integer"
          }
        },
        "required": [
          "max_sandboxes",
          "saved_snapshots",
          "usd"
        ],
        "type": "object"
      },
      "PortWait": {
        "description": "The answer to `POST /sandboxes/{name}/wait`.",
        "properties": {
          "port": {
            "format": "uint16",
            "minimum": 0.0,
            "type": "integer"
          },
          "ready": {
            "description": "true: something listens on the port (on any address); false: the timeout passed first.",
            "type": "boolean"
          },
          "waited_ms": {
            "description": "How long it waited, in milliseconds.",
            "format": "uint64",
            "minimum": 0.0,
            "type": "integer"
          }
        },
        "required": [
          "port",
          "ready",
          "waited_ms"
        ],
        "type": "object"
      },
      "Ports": {
        "description": "Inbound ports open on a sandbox's public IPv6 address (`GET /sandboxes/{name}/ports`). They stay open across stops and starts; only clients with IPv6 can reach them.",
        "properties": {
          "from_urls": {
            "default": [],
            "description": "The ones open because a public URL serves that TCP port: they close with the URL (or when it's made private) unless they were also opened on their own.",
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "ports": {
            "description": "Everything open besides SSH, as \"8080/tcp\" or \"27015/udp\", sorted.",
            "items": {
              "type": "string"
            },
            "type": "array"
          }
        },
        "required": [
          "ports"
        ],
        "type": "object"
      },
      "ProcessInfo": {
        "description": "A process in a sandbox (`GET /sandboxes/{name}/processes`), busiest first.",
        "properties": {
          "command": {
            "type": "string"
          },
          "command_id": {
            "description": "The history record of the command this process belongs to, if any.",
            "type": [
              "string",
              "null"
            ]
          },
          "cpu_percent": {
            "format": "double",
            "type": "number"
          },
          "memory_mb": {
            "format": "double",
            "type": "number"
          },
          "pid": {
            "format": "uint32",
            "minimum": 0.0,
            "type": "integer"
          },
          "user": {
            "type": "string"
          }
        },
        "required": [
          "command",
          "cpu_percent",
          "memory_mb",
          "pid",
          "user"
        ],
        "type": "object"
      },
      "ProcessList": {
        "description": "`live`: false when the sandbox isn't running and this is the last sample kept (`sampled_at`).",
        "properties": {
          "live": {
            "type": "boolean"
          },
          "processes": {
            "items": {
              "$ref": "#/components/schemas/ProcessInfo"
            },
            "type": "array"
          },
          "sampled_at": {
            "format": "int64",
            "type": [
              "integer",
              "null"
            ]
          },
          "total": {
            "format": "uint32",
            "minimum": 0.0,
            "type": "integer"
          }
        },
        "required": [
          "live",
          "processes",
          "total"
        ],
        "type": "object"
      },
      "PromptRequest": {
        "properties": {
          "agent": {
            "description": "A harness from `agents::HARNESSES` (default \"claude\").",
            "type": [
              "string",
              "null"
            ]
          },
          "effort": {
            "description": "Reasoning effort / thinking level, for harnesses that take one (claude, codex, pi).",
            "type": [
              "string",
              "null"
            ]
          },
          "model": {
            "description": "Passed to the harness's model flag, e.g. \"opus\", \"gpt-5.1-codex\", \"anthropic/claude-sonnet-4-5\".",
            "type": [
              "string",
              "null"
            ]
          },
          "prompt": {
            "type": "string"
          },
          "timeout_secs": {
            "format": "uint64",
            "minimum": 0.0,
            "type": [
              "integer",
              "null"
            ]
          }
        },
        "required": [
          "prompt"
        ],
        "type": "object"
      },
      "PublicUrl": {
        "description": "A public HTTPS URL for a port inside a sandbox.",
        "properties": {
          "created_at": {
            "format": "int64",
            "type": "integer"
          },
          "port": {
            "format": "uint16",
            "minimum": 0.0,
            "type": "integer"
          },
          "public": {
            "default": true,
            "description": "false: only people with the token (in the URL, or the `__Host-froog_url` cookie) get through.",
            "type": "boolean"
          },
          "title": {
            "description": "A label for people, e.g. \"API docs\".",
            "type": [
              "string",
              "null"
            ]
          },
          "url": {
            "description": "For a private URL, includes `?_token=…`: treat it like a password.",
            "type": "string"
          }
        },
        "required": [
          "created_at",
          "port",
          "url"
        ],
        "type": "object"
      },
      "Rate": {
        "description": "What a size costs to run, billed per second.",
        "properties": {
          "per_hour_usd": {
            "format": "double",
            "type": "number"
          },
          "per_month_usd": {
            "description": "A full month (730 hours).",
            "format": "double",
            "type": "number"
          },
          "size": {
            "$ref": "#/components/schemas/Size"
          }
        },
        "required": [
          "per_hour_usd",
          "per_month_usd",
          "size"
        ],
        "type": "object"
      },
      "Restore": {
        "properties": {
          "snapshot": {
            "type": "string"
          }
        },
        "required": [
          "snapshot"
        ],
        "type": "object"
      },
      "Sandbox": {
        "properties": {
          "auto_start": {
            "default": false,
            "description": "While stopped, a request to one of its public URLs, or an exec, file, job or wait call, starts it (billed and limited like any start).",
            "type": "boolean"
          },
          "auto_stop_at": {
            "description": "While running with an auto-stop timer: when it stops (unix seconds). A running agent prompt moves it one `ttl_secs` later; `PATCH ttl_secs` sets it to now + ttl_secs.",
            "format": "int64",
            "type": [
              "integer",
              "null"
            ]
          },
          "backend": {
            "type": "string"
          },
          "created_at": {
            "description": "Unix seconds.",
            "format": "int64",
            "type": "integer"
          },
          "delete_after_secs": {
            "default": 0,
            "description": "Deleted after staying stopped this many seconds. 0 = never.",
            "format": "uint64",
            "minimum": 0.0,
            "type": "integer"
          },
          "delete_at": {
            "description": "While stopped with `delete_after_secs`: when it will be deleted (unix seconds).",
            "format": "int64",
            "type": [
              "integer",
              "null"
            ]
          },
          "disk_expired": {
            "default": false,
            "description": "Set once the disk of a stopped sandbox was deleted; start and fork then fail.",
            "type": "boolean"
          },
          "disk_expires_at": {
            "default": null,
            "description": "For a stopped sandbox: when its disk will be deleted (save a snapshot to keep it longer).",
            "format": "int64",
            "type": [
              "integer",
              "null"
            ]
          },
          "env": {
            "description": "Names of its own environment variables (values are never returned).",
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "error": {
            "type": [
              "string",
              "null"
            ]
          },
          "ipv4": {
            "type": [
              "string",
              "null"
            ]
          },
          "ipv6": {
            "description": "Its own public IPv6 address (inbound: SSH and its open ports, `/sandboxes/{name}/ports`). None while stopped; it can change at a start (not at a warm resume).",
            "type": [
              "string",
              "null"
            ]
          },
          "labels": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "Your own key-value tags (`label=key=value` filters lists).",
            "type": "object"
          },
          "last_active_at": {
            "format": "int64",
            "type": "integer"
          },
          "max_lifetime_secs": {
            "default": 0,
            "description": "Stopped this many seconds after each start, busy or not. 0 = no limit.",
            "format": "uint64",
            "minimum": 0.0,
            "type": "integer"
          },
          "monitoring": {
            "default": true,
            "description": "false: no command history or process samples.",
            "type": "boolean"
          },
          "name": {
            "type": "string"
          },
          "os": {
            "$ref": "#/components/schemas/Os",
            "default": "ubuntu-24.04"
          },
          "period_cost_usd": {
            "description": "What its running time cost in the current billing period, at list prices (in lists and info; saved snapshots are billed per account, not here).",
            "format": "double",
            "type": [
              "number",
              "null"
            ]
          },
          "repo": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/SandboxRepo"
              },
              {
                "type": "null"
              }
            ],
            "description": "The repo cloned into this sandbox at create (`CreateSandbox.repo`)."
          },
          "setup_error": {
            "description": "Why setup failed: its error lines (a failed clone, a script's exit code).",
            "type": [
              "string",
              "null"
            ]
          },
          "setup_status": {
            "default": "none",
            "description": "The setup job (repos, the environment's setup script, `new --setup`) of the last boot: none | running | done | failed.",
            "type": "string"
          },
          "size": {
            "$ref": "#/components/schemas/Size"
          },
          "snapshots": {
            "default": true,
            "description": "false: never snapshotted; a stop erases it (no start, fork or restore).",
            "type": "boolean"
          },
          "ssh_port": {
            "default": 22,
            "description": "SSH port at `ipv4` (VMs on a shared host each have their own).",
            "format": "uint16",
            "minimum": 0.0,
            "type": "integer"
          },
          "started_at": {
            "format": "int64",
            "type": [
              "integer",
              "null"
            ]
          },
          "state": {
            "$ref": "#/components/schemas/State"
          },
          "stop_at": {
            "description": "While running with `max_lifetime_secs`: when it will be stopped (unix seconds).",
            "format": "int64",
            "type": [
              "integer",
              "null"
            ]
          },
          "stop_id": {
            "description": "Only in the answer to `POST /sandboxes/{name}/stop`: the stop's id, for `GET /stops/{id}`.",
            "type": [
              "string",
              "null"
            ]
          },
          "ttl_secs": {
            "description": "The auto-stop timer: it stops this many seconds after each start (or resume), busy or not, unless an agent prompt is running. 0 = no auto-stop.",
            "format": "uint64",
            "minimum": 0.0,
            "type": "integer"
          },
          "warm_until": {
            "description": "Stopped, with its memory kept until then: a start before it resumes every running program in about a second (after it, the start boots from the disk snapshot).",
            "format": "int64",
            "type": [
              "integer",
              "null"
            ]
          }
        },
        "required": [
          "backend",
          "created_at",
          "last_active_at",
          "name",
          "size",
          "state",
          "ttl_secs"
        ],
        "type": "object"
      },
      "SandboxMetrics": {
        "description": "`GET /sandboxes/{name}/metrics`: one sample read from the running sandbox now.",
        "properties": {
          "cpu_count": {
            "format": "uint32",
            "minimum": 0.0,
            "type": "integer"
          },
          "cpu_percent": {
            "description": "Share of all CPUs busy over half a second (0-100).",
            "format": "double",
            "type": "number"
          },
          "disk_total_bytes": {
            "format": "uint64",
            "minimum": 0.0,
            "type": "integer"
          },
          "disk_used_bytes": {
            "description": "Of the root filesystem.",
            "format": "uint64",
            "minimum": 0.0,
            "type": "integer"
          },
          "io_pressure": {
            "description": "Share of the last minute some task waited for disk I/O (0-1).",
            "format": "double",
            "type": "number"
          },
          "memory_total_bytes": {
            "format": "uint64",
            "minimum": 0.0,
            "type": "integer"
          },
          "memory_used_bytes": {
            "format": "uint64",
            "minimum": 0.0,
            "type": "integer"
          },
          "network_rx_bytes": {
            "description": "Received and sent since boot, all interfaces but loopback.",
            "format": "uint64",
            "minimum": 0.0,
            "type": "integer"
          },
          "network_tx_bytes": {
            "format": "uint64",
            "minimum": 0.0,
            "type": "integer"
          },
          "sampled_at": {
            "description": "Unix seconds.",
            "format": "int64",
            "type": "integer"
          }
        },
        "required": [
          "cpu_count",
          "cpu_percent",
          "disk_total_bytes",
          "disk_used_bytes",
          "io_pressure",
          "memory_total_bytes",
          "memory_used_bytes",
          "network_rx_bytes",
          "network_tx_bytes",
          "sampled_at"
        ],
        "type": "object"
      },
      "SandboxRepo": {
        "description": "A repo cloned into one sandbox (`CreateSandbox.repo`), like the account's repos: at boot, when the directory has no checkout yet. Credentials come from the sandbox's environment (`GH_TOKEN`, `~/.git-credentials`), never the URL.",
        "properties": {
          "branch": {
            "description": "Branch or tag to check out (default: the repo's default branch).",
            "type": [
              "string",
              "null"
            ]
          },
          "dir": {
            "description": "Directory relative to the home directory (default: the repo's name).",
            "type": [
              "string",
              "null"
            ]
          },
          "url": {
            "description": "`https://`, `ssh://` or `git@` URL.",
            "type": "string"
          }
        },
        "required": [
          "url"
        ],
        "type": "object"
      },
      "SandboxUsage": {
        "description": "`GET /sandboxes/{name}/usage`: one sandbox's running time and cost in a window.",
        "properties": {
          "cost_usd": {
            "description": "At list prices (saved snapshots are billed per account, not here).",
            "format": "double",
            "type": "number"
          },
          "from": {
            "description": "The window, unix seconds (`to` is at most now).",
            "format": "int64",
            "type": "integer"
          },
          "lines": {
            "description": "One line per size it ran at.",
            "items": {
              "$ref": "#/components/schemas/UsageLine"
            },
            "type": "array"
          },
          "name": {
            "type": "string"
          },
          "seconds": {
            "description": "Running time in the window, billed per second.",
            "format": "uint64",
            "minimum": 0.0,
            "type": "integer"
          },
          "to": {
            "format": "int64",
            "type": "integer"
          }
        },
        "required": [
          "cost_usd",
          "from",
          "lines",
          "name",
          "seconds",
          "to"
        ],
        "type": "object"
      },
      "SavedSnapshot": {
        "description": "A saved snapshot of the account (`GET /saved-snapshots`): a template for `new --from NAME`. It outlives its source sandbox.",
        "properties": {
          "created_at": {
            "format": "int64",
            "type": "integer"
          },
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "offsite": {
            "default": false,
            "description": "A copy is kept off the host, on separate storage (encrypted).",
            "type": "boolean"
          },
          "os": {
            "$ref": "#/components/schemas/Os"
          },
          "size": {
            "$ref": "#/components/schemas/Size"
          },
          "size_gb": {
            "format": "double",
            "type": [
              "number",
              "null"
            ]
          },
          "source": {
            "description": "The sandbox it was saved from.",
            "type": "string"
          },
          "source_deleted": {
            "description": "The source sandbox was deleted (the saved snapshot stays).",
            "type": "boolean"
          },
          "status": {
            "description": "creating | available",
            "type": "string"
          }
        },
        "required": [
          "created_at",
          "id",
          "name",
          "os",
          "size",
          "source",
          "source_deleted",
          "status"
        ],
        "type": "object"
      },
      "SavedSnapshotAllowance": {
        "description": "Saved snapshots the plan includes and how many the account keeps (`GET /limits`).",
        "properties": {
          "extra": {
            "description": "Above the included ones, billed per day.",
            "format": "uint64",
            "minimum": 0.0,
            "type": "integer"
          },
          "included": {
            "format": "uint64",
            "minimum": 0.0,
            "type": "integer"
          },
          "price_per_extra_day_usd": {
            "format": "double",
            "type": "number"
          },
          "used": {
            "description": "Saved snapshots (names) the account keeps now.",
            "format": "uint64",
            "minimum": 0.0,
            "type": "integer"
          }
        },
        "required": [
          "extra",
          "included",
          "price_per_extra_day_usd",
          "used"
        ],
        "type": "object"
      },
      "SetEnvFile": {
        "description": "`PUT /environment/files`",
        "properties": {
          "content": {
            "type": "string"
          },
          "path": {
            "type": "string"
          }
        },
        "required": [
          "content",
          "path"
        ],
        "type": "object"
      },
      "SetEnvVar": {
        "properties": {
          "value": {
            "type": "string"
          }
        },
        "required": [
          "value"
        ],
        "type": "object"
      },
      "SetPorts": {
        "description": "`PUT /sandboxes/{name}/ports`: the ports to open, replacing the ones opened before (\"8080/tcp\", \"27015/udp\"; a bare number is TCP). Public URLs' ports stay open regardless.",
        "properties": {
          "ports": {
            "items": {
              "type": "string"
            },
            "type": "array"
          }
        },
        "required": [
          "ports"
        ],
        "type": "object"
      },
      "SetTelemetry": {
        "description": "`PUT /telemetry`: the collector's base URL (froog adds /v1/metrics and /v1/logs) and the headers your vendor needs, e.g. `{\"dd-api-key\": \"…\"}`.",
        "properties": {
          "endpoint": {
            "type": "string"
          },
          "headers": {
            "additionalProperties": {
              "type": "string"
            },
            "default": {},
            "type": "object"
          }
        },
        "required": [
          "endpoint"
        ],
        "type": "object"
      },
      "Size": {
        "description": "Machine sizes. Mapped to provider server types by the server's config.",
        "oneOf": [
          {
            "enum": [
              "small",
              "default",
              "large",
              "xlarge"
            ],
            "type": "string"
          },
          {
            "description": "No longer offered (was the $20 always-on VM: 2 vCPU, 8 GB, 100 GB).",
            "enum": [
              "standard"
            ],
            "type": "string"
          }
        ]
      },
      "Snapshot": {
        "properties": {
          "created_at": {
            "format": "int64",
            "type": "integer"
          },
          "id": {
            "type": "string"
          },
          "label": {
            "type": [
              "string",
              "null"
            ]
          },
          "offsite": {
            "default": false,
            "description": "A copy is kept off the host, on separate storage (encrypted), so the snapshot survives the loss of the machine. Usually true within minutes of the snapshot being taken.",
            "type": "boolean"
          },
          "sandbox": {
            "description": "The sandbox it belongs to (in account-wide lists and single lookups).",
            "type": [
              "string",
              "null"
            ]
          },
          "saved": {
            "description": "Saved snapshots are kept until deleted and billed per day; automatic ones are free.",
            "type": "boolean"
          },
          "size_gb": {
            "format": "double",
            "type": [
              "number",
              "null"
            ]
          },
          "status": {
            "type": "string"
          }
        },
        "required": [
          "created_at",
          "id",
          "saved",
          "status"
        ],
        "type": "object"
      },
      "SshInfo": {
        "properties": {
          "host": {
            "type": "string"
          },
          "host_key": {
            "description": "OpenSSH public host key line, to pin in known_hosts.",
            "type": "string"
          },
          "port": {
            "format": "uint16",
            "minimum": 0.0,
            "type": "integer"
          },
          "user": {
            "type": "string"
          }
        },
        "required": [
          "host",
          "host_key",
          "port",
          "user"
        ],
        "type": "object"
      },
      "SshKey": {
        "properties": {
          "created_at": {
            "format": "int64",
            "type": "integer"
          },
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "public_key": {
            "type": "string"
          }
        },
        "required": [
          "created_at",
          "id",
          "name",
          "public_key"
        ],
        "type": "object"
      },
      "StartLimits": {
        "description": "Sandbox starts (create, start, fork, restore) allowed per window; 0 = no limit.",
        "properties": {
          "per_day": {
            "format": "uint32",
            "minimum": 0.0,
            "type": "integer"
          },
          "per_hour": {
            "format": "uint32",
            "minimum": 0.0,
            "type": "integer"
          },
          "per_minute": {
            "format": "uint32",
            "minimum": 0.0,
            "type": "integer"
          },
          "used_day": {
            "format": "uint32",
            "minimum": 0.0,
            "type": "integer"
          },
          "used_hour": {
            "format": "uint32",
            "minimum": 0.0,
            "type": "integer"
          },
          "used_minute": {
            "format": "uint32",
            "minimum": 0.0,
            "type": "integer"
          }
        },
        "required": [
          "per_day",
          "per_hour",
          "per_minute",
          "used_day",
          "used_hour",
          "used_minute"
        ],
        "type": "object"
      },
      "StartSandbox": {
        "description": "Optional body of `POST /sandboxes/{name}/start`.",
        "properties": {
          "env": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "Environment variables set on top of the sandbox's own (these win; kept).",
            "type": [
              "object",
              "null"
            ]
          },
          "environment": {
            "description": "false: boot without the account's environment (env vars, repos, secret files, setup script, in-sandbox CLI key); true: with it. Default: as before. Kept for later starts.",
            "type": [
              "boolean",
              "null"
            ]
          },
          "size": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Size"
              },
              {
                "type": "null"
              }
            ],
            "description": "Start at a different size."
          },
          "ttl_secs": {
            "description": "A new auto-stop timer in seconds (0 = no auto-stop). Default: keep it.",
            "format": "uint64",
            "minimum": 0.0,
            "type": [
              "integer",
              "null"
            ]
          }
        },
        "type": "object"
      },
      "State": {
        "enum": [
          "creating",
          "running",
          "stopping",
          "stopped",
          "starting",
          "restoring",
          "deleting",
          "deleted",
          "error"
        ],
        "type": "string"
      },
      "Stop": {
        "description": "A stop (`GET /stops/{id}`): `POST /sandboxes/{name}/stop` returns its id, and an idle or billing stop gets one too. Stopping again while one is pending returns the same id.",
        "properties": {
          "ended_at": {
            "format": "int64",
            "type": [
              "integer",
              "null"
            ]
          },
          "error": {
            "type": [
              "string",
              "null"
            ]
          },
          "id": {
            "description": "`stop_…`",
            "type": "string"
          },
          "requested_at": {
            "description": "Unix seconds.",
            "format": "int64",
            "type": "integer"
          },
          "sandbox": {
            "description": "The sandbox's name.",
            "type": "string"
          },
          "status": {
            "description": "pending (snapshotting and shutting down) | completed (stopped, or erased when it had snapshots off) | failed (see `error`; the sandbox is in the error state, a start retries from its last snapshot).",
            "type": "string"
          }
        },
        "required": [
          "id",
          "requested_at",
          "sandbox",
          "status"
        ],
        "type": "object"
      },
      "Telemetry": {
        "description": "`GET /telemetry`: the account's OpenTelemetry endpoint (header values are never returned).",
        "properties": {
          "endpoint": {
            "type": "string"
          },
          "headers": {
            "description": "Header names only.",
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "last_error": {
            "type": [
              "string",
              "null"
            ]
          },
          "last_export_at": {
            "format": "int64",
            "type": [
              "integer",
              "null"
            ]
          }
        },
        "required": [
          "endpoint",
          "headers"
        ],
        "type": "object"
      },
      "TelemetrySaved": {
        "properties": {
          "telemetry": {
            "$ref": "#/components/schemas/Telemetry"
          },
          "test": {
            "$ref": "#/components/schemas/TelemetryTest"
          }
        },
        "required": [
          "telemetry",
          "test"
        ],
        "type": "object"
      },
      "TelemetryTest": {
        "description": "The collector's answer to the test export sent on save.",
        "properties": {
          "message": {
            "type": [
              "string",
              "null"
            ]
          },
          "ok": {
            "type": "boolean"
          },
          "status": {
            "format": "uint16",
            "minimum": 0.0,
            "type": [
              "integer",
              "null"
            ]
          }
        },
        "required": [
          "ok"
        ],
        "type": "object"
      },
      "Trial": {
        "properties": {
          "active": {
            "description": "In the trial now. Before choosing a plan this is false and `ends_at` None: the trial starts with the first plan.",
            "type": "boolean"
          },
          "blocked": {
            "description": "The card already had a free trial on another account (`trial_blocked`): the plan is paid from the start.",
            "type": "boolean"
          },
          "days": {
            "format": "uint64",
            "minimum": 0.0,
            "type": "integer"
          },
          "ends_at": {
            "description": "Unix seconds; None until the trial starts.",
            "format": "int64",
            "type": [
              "integer",
              "null"
            ]
          },
          "hours": {
            "default": 0,
            "description": "Hours of running time the trial includes (all sandboxes together).",
            "format": "uint64",
            "minimum": 0.0,
            "type": "integer"
          },
          "hours_used": {
            "default": 0.0,
            "description": "Hours used so far.",
            "format": "double",
            "type": "number"
          },
          "max_auto_stop_secs": {
            "default": 0,
            "description": "The longest auto-stop timer during the trial, in seconds (also its default): the trial needs one (`trial_auto_stop_required`). It can't save new saved snapshots either.",
            "format": "uint64",
            "minimum": 0.0,
            "type": "integer"
          },
          "max_sandboxes": {
            "description": "Sandboxes at once during the trial.",
            "format": "uint32",
            "minimum": 0.0,
            "type": "integer"
          }
        },
        "required": [
          "active",
          "days",
          "max_sandboxes"
        ],
        "type": "object"
      },
      "UpdateDataRetention": {
        "properties": {
          "confirmation": {
            "description": "Turning zero data retention on erases the data of every stopped sandbox: send \"delete stopped sandbox data\" to confirm.",
            "type": [
              "string",
              "null"
            ]
          },
          "monitoring_off": {
            "type": [
              "boolean",
              "null"
            ]
          },
          "snapshots_off": {
            "type": [
              "boolean",
              "null"
            ]
          },
          "zero_data_retention": {
            "type": [
              "boolean",
              "null"
            ]
          }
        },
        "type": "object"
      },
      "UpdateEnvironment": {
        "description": "`PATCH /environment`: fields left out stay as they are; `setup: \"\"` removes the script.",
        "properties": {
          "repos": {
            "items": {
              "type": "string"
            },
            "type": [
              "array",
              "null"
            ]
          },
          "setup": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "type": "object"
      },
      "UpdateSandbox": {
        "description": "`PATCH /sandboxes/{name}`: rename, change the auto-stop timer, or change labels.",
        "properties": {
          "auto_start": {
            "description": "true: while stopped, a request to one of its public URLs, or an exec, file, job or wait call, starts it; false: off.",
            "type": [
              "boolean",
              "null"
            ]
          },
          "delete_after_secs": {
            "description": "Delete it after staying stopped this many seconds (0 = never).",
            "format": "uint64",
            "minimum": 0.0,
            "type": [
              "integer",
              "null"
            ]
          },
          "labels": {
            "additionalProperties": {
              "type": [
                "string",
                "null"
              ]
            },
            "description": "Labels to set; a null value removes that label. Labels not named stay.",
            "type": [
              "object",
              "null"
            ]
          },
          "max_lifetime_secs": {
            "description": "Stop it this many seconds after each start (0 = no limit). Set while it runs, it counts from that start.",
            "format": "uint64",
            "minimum": 0.0,
            "type": [
              "integer",
              "null"
            ]
          },
          "name": {
            "type": [
              "string",
              "null"
            ]
          },
          "ttl_secs": {
            "description": "A new auto-stop timer in seconds (0 = no auto-stop). While running, the sandbox then stops this long from now (`froog extend --hours`); later starts use it too.",
            "format": "uint64",
            "minimum": 0.0,
            "type": [
              "integer",
              "null"
            ]
          }
        },
        "type": "object"
      },
      "UpdateWebhook": {
        "description": "`PATCH /webhooks/{id}`: change what's given. The signing secret stays (rotate changes it).",
        "properties": {
          "enabled": {
            "type": [
              "boolean",
              "null"
            ]
          },
          "events": {
            "description": "Empty = every event.",
            "items": {
              "type": "string"
            },
            "type": [
              "array",
              "null"
            ]
          },
          "name": {
            "description": "An empty name removes it.",
            "type": [
              "string",
              "null"
            ]
          },
          "url": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "type": "object"
      },
      "UrlToken": {
        "description": "A viewer token of a private URL (`GET /sandboxes/{name}/urls/{port}/tokens`).",
        "properties": {
          "created_at": {
            "format": "int64",
            "type": "integer"
          },
          "expires_at": {
            "format": "int64",
            "type": "integer"
          },
          "id": {
            "description": "`utk_…`, for revoking it.",
            "type": "string"
          },
          "label": {
            "type": [
              "string",
              "null"
            ]
          },
          "token": {
            "description": "Only when created: the secret (`pvt_…`). Send it as `?_token=` (the proxy turns it into a cookie) or in the `X-Froog-Url-Token` header.",
            "type": [
              "string",
              "null"
            ]
          },
          "url": {
            "description": "Only when created: the URL with `?_token=` for this token.",
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "created_at",
          "expires_at",
          "id"
        ],
        "type": "object"
      },
      "Usage": {
        "properties": {
          "billable_usd": {
            "description": "Usage past the plan, taken from the wallet.",
            "format": "double",
            "type": "number"
          },
          "free_trial_until": {
            "description": "The period is the free trial: nothing is charged before this time (unix seconds).",
            "format": "int64",
            "type": [
              "integer",
              "null"
            ]
          },
          "from": {
            "default": 0,
            "description": "The period, unix seconds: the current billing period (or the trial), or the calendar month asked for. `to` is now for a period still running.",
            "format": "int64",
            "type": "integer"
          },
          "included_usd": {
            "default": 0.0,
            "description": "What the plan paid for this period covers.",
            "format": "double",
            "type": "number"
          },
          "lines": {
            "items": {
              "$ref": "#/components/schemas/UsageLine"
            },
            "type": "array"
          },
          "month": {
            "description": "\"YYYY-MM\" of the period's start.",
            "type": "string"
          },
          "saved_snapshot_days": {
            "format": "double",
            "type": "number"
          },
          "snapshot_cost_usd": {
            "format": "double",
            "type": "number"
          },
          "to": {
            "default": 0,
            "format": "int64",
            "type": "integer"
          },
          "total_usd": {
            "description": "Everything used in the period, at list prices.",
            "format": "double",
            "type": "number"
          }
        },
        "required": [
          "billable_usd",
          "lines",
          "month",
          "saved_snapshot_days",
          "snapshot_cost_usd",
          "total_usd"
        ],
        "type": "object"
      },
      "UsageLine": {
        "properties": {
          "backend": {
            "type": "string"
          },
          "cost_usd": {
            "format": "double",
            "type": "number"
          },
          "seconds": {
            "description": "Running time in the period, billed per second.",
            "format": "uint64",
            "minimum": 0.0,
            "type": "integer"
          },
          "size": {
            "$ref": "#/components/schemas/Size"
          }
        },
        "required": [
          "backend",
          "cost_usd",
          "seconds",
          "size"
        ],
        "type": "object"
      },
      "WaitForPort": {
        "description": "`POST /sandboxes/{name}/wait`: wait until something listens on a TCP port inside the sandbox.",
        "properties": {
          "port": {
            "format": "uint16",
            "minimum": 0.0,
            "type": "integer"
          },
          "timeout_secs": {
            "description": "Give up after this many seconds (1-600, default 60). Time spent starting counts.",
            "format": "uint64",
            "minimum": 0.0,
            "type": [
              "integer",
              "null"
            ]
          }
        },
        "required": [
          "port"
        ],
        "type": "object"
      },
      "Waitlist": {
        "properties": {
          "email": {
            "type": "string"
          },
          "note": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "email"
        ],
        "type": "object"
      },
      "WalletPay": {
        "description": "`POST /v1/billing/wallet/pay`: charge the saved card and add the money to the wallet.",
        "properties": {
          "amount_usd": {
            "description": "Whole dollars, from `Billing.min_topup_usd` to `Billing.max_wallet_per_day_usd` (also the most charged in any 24 hours).",
            "format": "uint32",
            "minimum": 0.0,
            "type": "integer"
          }
        },
        "required": [
          "amount_usd"
        ],
        "type": "object"
      },
      "Webhook": {
        "description": "A webhook endpoint (the signing secret is shown only when created or rotated).",
        "properties": {
          "created_at": {
            "format": "int64",
            "type": "integer"
          },
          "enabled": {
            "default": true,
            "description": "false: paused, nothing is sent (test pings still are).",
            "type": "boolean"
          },
          "events": {
            "description": "Empty = every event.",
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "id": {
            "type": "string"
          },
          "name": {
            "description": "Your own name for it (up to 100 characters).",
            "type": [
              "string",
              "null"
            ]
          },
          "updated_at": {
            "format": "int64",
            "type": [
              "integer",
              "null"
            ]
          },
          "url": {
            "type": "string"
          }
        },
        "required": [
          "created_at",
          "events",
          "id",
          "url"
        ],
        "type": "object"
      },
      "WebhookDelivery": {
        "description": "One delivery attempt of a webhook (`GET /webhooks/{id}/deliveries`, kept 30 days).",
        "properties": {
          "attempt": {
            "description": "1-8 (`froog-attempt`).",
            "format": "uint32",
            "minimum": 0.0,
            "type": "integer"
          },
          "created_at": {
            "description": "Unix seconds.",
            "format": "int64",
            "type": "integer"
          },
          "duration_ms": {
            "format": "uint64",
            "minimum": 0.0,
            "type": "integer"
          },
          "error": {
            "description": "What went wrong (a connection error, a non-2xx answer).",
            "type": [
              "string",
              "null"
            ]
          },
          "event": {
            "description": "sandbox.running, …, or ping.",
            "type": "string"
          },
          "event_id": {
            "description": "The event id (`evt_…`, also the `froog-delivery` header): the same on every attempt.",
            "type": "string"
          },
          "next_attempt_at": {
            "description": "When the next attempt is due, if one is.",
            "format": "int64",
            "type": [
              "integer",
              "null"
            ]
          },
          "response_code": {
            "description": "The endpoint's HTTP status; none when it couldn't be reached.",
            "format": "uint16",
            "minimum": 0.0,
            "type": [
              "integer",
              "null"
            ]
          },
          "sandbox": {
            "description": "The sandbox it was about (none for a ping).",
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "description": "delivered | failed (another attempt follows unless this was the last).",
            "type": "string"
          }
        },
        "required": [
          "attempt",
          "created_at",
          "duration_ms",
          "event",
          "event_id",
          "status"
        ],
        "type": "object"
      },
      "WebhookSecret": {
        "properties": {
          "events": {
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "id": {
            "type": "string"
          },
          "name": {
            "type": [
              "string",
              "null"
            ]
          },
          "secret": {
            "description": "Verify `froog-signature` with this; shown once.",
            "type": "string"
          },
          "url": {
            "type": "string"
          }
        },
        "required": [
          "events",
          "id",
          "secret",
          "url"
        ],
        "type": "object"
      }
    },
    "securitySchemes": {
      "bearer": {
        "description": "An API key (froog_…) from the dashboard or `froog api-key create`",
        "scheme": "bearer",
        "type": "http"
      }
    }
  },
  "info": {
    "description": "Cloud sandboxes for AI agents: full Linux VMs you create, run commands in, snapshot, fork and stop. Authenticate with an API key as a bearer token. Each client IP may send 10,000 requests per 60 s (RateLimit-* headers on every answer; past it, 429 ip_rate_limited with Retry-After). Docs: https://froog.tech/docs/",
    "title": "froog API",
    "version": "0.9.1"
  },
  "openapi": "3.1.0",
  "paths": {
    "/mcp": {
      "post": {
        "operationId": "mcp",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/JsonRpcRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/JsonRpcResponse"
                }
              }
            },
            "description": "OK"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "MCP server (Streamable HTTP, stateless): JSON-RPC 2.0 initialize, tools/list and tools/call, with tools to list, create, run commands in, fork, snapshot, stop and delete sandboxes, read and write files and host ports. A scoped key may call only the tools its actions allow",
        "tags": [
          "MCP"
        ]
      }
    },
    "/v1/account": {
      "get": {
        "operationId": "getAccount",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Account"
                }
              }
            },
            "description": "OK"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "The signed-in account",
        "tags": [
          "Account"
        ]
      }
    },
    "/v1/account/data-retention": {
      "get": {
        "operationId": "getDataRetention",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DataRetention"
                }
              }
            },
            "description": "OK"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "Data controls: zero data retention, snapshots off, monitoring off",
        "tags": [
          "Account"
        ]
      },
      "patch": {
        "operationId": "updateDataRetention",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateDataRetention"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DataRetention"
                }
              }
            },
            "description": "OK"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "Change data controls (dashboard session only)",
        "tags": [
          "Account"
        ]
      }
    },
    "/v1/api-keys": {
      "get": {
        "operationId": "listApiKeys",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "items": {
                    "$ref": "#/components/schemas/ApiKeyInfo"
                  },
                  "type": "array"
                }
              }
            },
            "description": "OK"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "API keys (secrets are never returned)",
        "tags": [
          "Keys"
        ]
      },
      "post": {
        "operationId": "createApiKey",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/NewApiKey"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreatedApiKey"
                }
              }
            },
            "description": "OK"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "Create an API key: a preset or actions, sandboxes or labels, expiry",
        "tags": [
          "Keys"
        ]
      }
    },
    "/v1/api-keys/{id}": {
      "delete": {
        "operationId": "revokeApiKey",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Done"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "Revoke an API key",
        "tags": [
          "Keys"
        ]
      }
    },
    "/v1/api-keys/{id}/rotate": {
      "post": {
        "operationId": "rotateApiKey",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreatedApiKey"
                }
              }
            },
            "description": "OK"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "A new secret for a key (same id, scope and expiry)",
        "tags": [
          "Keys"
        ]
      }
    },
    "/v1/audit": {
      "get": {
        "operationId": "listAuditEvents",
        "parameters": [
          {
            "description": "1-200, default 50",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "next_cursor of the previous page",
            "in": "query",
            "name": "cursor",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuditList"
                }
              }
            },
            "description": "OK"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "The account's audit log, newest first (90 days): who created, deleted or changed what, from which IP",
        "tags": [
          "Account"
        ]
      }
    },
    "/v1/billing": {
      "get": {
        "operationId": "getBilling",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Billing"
                }
              }
            },
            "description": "OK"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "Plan, rates, this period's usage, wallet and auto top-up",
        "tags": [
          "Account"
        ]
      }
    },
    "/v1/billing/plan": {
      "delete": {
        "operationId": "cancelPlan",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Billing"
                }
              }
            },
            "description": "OK"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "Cancel the plan at the end of the period",
        "tags": [
          "Account"
        ]
      },
      "post": {
        "operationId": "choosePlan",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ChoosePlan"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlanChange"
                }
              }
            },
            "description": "OK"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "Choose or change the plan (the first one returns a Stripe Checkout page)",
        "tags": [
          "Account"
        ]
      }
    },
    "/v1/billing/portal": {
      "post": {
        "operationId": "billingPortal",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BillingUrl"
                }
              }
            },
            "description": "OK"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "A Stripe page to change the card and download invoices",
        "tags": [
          "Account"
        ]
      }
    },
    "/v1/billing/trial/end": {
      "post": {
        "operationId": "endTrial",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Billing"
                }
              }
            },
            "description": "OK"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "End the free trial now and start the plan",
        "tags": [
          "Account"
        ]
      }
    },
    "/v1/billing/wallet/auto-topup": {
      "put": {
        "operationId": "setAutoTopup",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AutoTopup"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Billing"
                }
              }
            },
            "description": "OK"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "Set auto top-up (0 = off)",
        "tags": [
          "Account"
        ]
      }
    },
    "/v1/billing/wallet/pay": {
      "post": {
        "operationId": "walletPay",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WalletPay"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Billing"
                }
              }
            },
            "description": "OK"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "Charge the saved card and add the money to the wallet",
        "tags": [
          "Account"
        ]
      }
    },
    "/v1/cli-login": {
      "post": {
        "operationId": "startCliLogin",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CliLoginRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CliLoginStarted"
                }
              }
            },
            "description": "OK"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "security": [],
        "summary": "Start a browser login for the CLI",
        "tags": [
          "Keys"
        ]
      }
    },
    "/v1/cli-login/poll": {
      "post": {
        "operationId": "pollCliLogin",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CliLoginPoll"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CliLoginResult"
                }
              }
            },
            "description": "OK"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "security": [],
        "summary": "Poll a CLI login (202 until approved)",
        "tags": [
          "Keys"
        ]
      }
    },
    "/v1/cli-login/{code}": {
      "delete": {
        "operationId": "denyCliLogin",
        "parameters": [
          {
            "in": "path",
            "name": "code",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Done"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "Deny a CLI login",
        "tags": [
          "Keys"
        ]
      },
      "get": {
        "operationId": "getCliLogin",
        "parameters": [
          {
            "in": "path",
            "name": "code",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CliLoginInfo"
                }
              }
            },
            "description": "OK"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "Describe a pending CLI login",
        "tags": [
          "Keys"
        ]
      }
    },
    "/v1/cli-login/{code}/approve": {
      "post": {
        "operationId": "approveCliLogin",
        "parameters": [
          {
            "in": "path",
            "name": "code",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Done"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "Approve a CLI login (creates a key)",
        "tags": [
          "Keys"
        ]
      }
    },
    "/v1/env": {
      "get": {
        "operationId": "listEnv",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "items": {
                    "$ref": "#/components/schemas/EnvVar"
                  },
                  "type": "array"
                }
              }
            },
            "description": "OK"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "Account environment variables (names and previews)",
        "tags": [
          "Environment"
        ]
      }
    },
    "/v1/env/{name}": {
      "delete": {
        "operationId": "removeEnv",
        "parameters": [
          {
            "in": "path",
            "name": "name",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Done"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "Remove an account environment variable",
        "tags": [
          "Environment"
        ]
      },
      "put": {
        "operationId": "setEnv",
        "parameters": [
          {
            "in": "path",
            "name": "name",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SetEnvVar"
              }
            }
          },
          "required": true
        },
        "responses": {
          "204": {
            "description": "Done"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "Set an account environment variable",
        "tags": [
          "Environment"
        ]
      }
    },
    "/v1/environment": {
      "get": {
        "operationId": "getEnvironment",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Environment"
                }
              }
            },
            "description": "OK"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "Repos, secret files and setup script every new sandbox gets",
        "tags": [
          "Environment"
        ]
      },
      "patch": {
        "operationId": "updateEnvironment",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateEnvironment"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Environment"
                }
              }
            },
            "description": "OK"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "Change repos or the setup script",
        "tags": [
          "Environment"
        ]
      }
    },
    "/v1/environment/files": {
      "delete": {
        "operationId": "removeEnvironmentFile",
        "parameters": [
          {
            "description": "the file, relative to ~",
            "in": "query",
            "name": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Done"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "Remove a secret file",
        "tags": [
          "Environment"
        ]
      },
      "put": {
        "operationId": "setEnvironmentFile",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SetEnvFile"
              }
            }
          },
          "required": true
        },
        "responses": {
          "204": {
            "description": "Done"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "Write a secret file",
        "tags": [
          "Environment"
        ]
      }
    },
    "/v1/feedback": {
      "post": {
        "operationId": "sendFeedback",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Feedback"
              }
            }
          },
          "required": true
        },
        "responses": {
          "204": {
            "description": "Done"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "Send feedback to the froog team",
        "tags": [
          "Account"
        ]
      }
    },
    "/v1/file-urls/{token}": {
      "get": {
        "operationId": "downloadSigned",
        "parameters": [
          {
            "in": "path",
            "name": "token",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/octet-stream": {
                "schema": {
                  "format": "binary",
                  "type": "string"
                }
              }
            },
            "description": "The file"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "security": [],
        "summary": "Download through a signed link",
        "tags": [
          "Files"
        ]
      },
      "put": {
        "operationId": "uploadSigned",
        "parameters": [
          {
            "in": "path",
            "name": "token",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/octet-stream": {
              "schema": {
                "format": "binary",
                "type": "string"
              }
            }
          },
          "required": true
        },
        "responses": {
          "204": {
            "description": "Done"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "security": [],
        "summary": "Upload through a signed link (raw bytes)",
        "tags": [
          "Files"
        ]
      }
    },
    "/v1/images": {
      "get": {
        "operationId": "listImages",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "items": {
                    "$ref": "#/components/schemas/OsImage"
                  },
                  "type": "array"
                }
              }
            },
            "description": "OK"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "Operating systems a sandbox can run",
        "tags": [
          "Account"
        ]
      }
    },
    "/v1/limits": {
      "get": {
        "operationId": "getLimits",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Limits"
                }
              }
            },
            "description": "OK"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "What the account can run now: sandboxes at once, sizes, trial, start limits",
        "tags": [
          "Account"
        ]
      }
    },
    "/v1/sandboxes": {
      "get": {
        "operationId": "listSandboxes",
        "parameters": [
          {
            "description": "key=value, or key for any value; repeat to require several",
            "in": "query",
            "name": "label",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "exact name",
            "in": "query",
            "name": "name",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "names that start with this",
            "in": "query",
            "name": "name_prefix",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "creating, starting, running, stopping, stopped, restoring, deleting or error; comma-separated for several",
            "in": "query",
            "name": "state",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "1-200; without it, every match. With it, X-Next-Cursor names the next page",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "X-Next-Cursor of the previous page (it is `<created_at>_<id>` of that page's last sandbox)",
            "in": "query",
            "name": "cursor",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "items": {
                    "$ref": "#/components/schemas/Sandbox"
                  },
                  "type": "array"
                }
              }
            },
            "description": "OK",
            "headers": {
              "X-Next-Cursor": {
                "description": "With limit, when more sandboxes follow: pass it as cursor",
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "Sandboxes, oldest first, filtered and paged",
        "tags": [
          "Sandboxes"
        ]
      },
      "post": {
        "operationId": "createSandboxes",
        "parameters": [
          {
            "description": "Any unique string (up to 255 characters). A retry with the same key within 24 hours returns the first response instead of creating more sandboxes; the same key with a different request is a 422 idempotency_key_reused.",
            "in": "header",
            "name": "Idempotency-Key",
            "required": false,
            "schema": {
              "maxLength": 255,
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateSandbox"
              }
            }
          },
          "required": true
        },
        "responses": {
          "202": {
            "content": {
              "application/json": {
                "schema": {
                  "items": {
                    "$ref": "#/components/schemas/Sandbox"
                  },
                  "type": "array"
                }
              }
            },
            "description": "OK"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "Create sandboxes (202; poll until running)",
        "tags": [
          "Sandboxes"
        ]
      }
    },
    "/v1/sandboxes/{name}": {
      "delete": {
        "operationId": "deleteSandbox",
        "parameters": [
          {
            "in": "path",
            "name": "name",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "202": {
            "description": "Done"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "Delete a sandbox (saved snapshots stay)",
        "tags": [
          "Sandboxes"
        ]
      },
      "get": {
        "operationId": "getSandbox",
        "parameters": [
          {
            "in": "path",
            "name": "name",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Sandbox"
                }
              }
            },
            "description": "OK"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "One sandbox",
        "tags": [
          "Sandboxes"
        ]
      },
      "patch": {
        "operationId": "updateSandbox",
        "parameters": [
          {
            "in": "path",
            "name": "name",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateSandbox"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Sandbox"
                }
              }
            },
            "description": "OK"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "Rename; change the auto-stop timer (counting from now while running), lifetime, auto-delete, auto-start or labels",
        "tags": [
          "Sandboxes"
        ]
      }
    },
    "/v1/sandboxes/{name}/browser": {
      "post": {
        "operationId": "getBrowserLink",
        "parameters": [
          {
            "in": "path",
            "name": "name",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "dark (default) or light: the viewer's look",
            "in": "query",
            "name": "theme",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BrowserRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DesktopLink"
                }
              }
            },
            "description": "OK"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "A link to the browser view: only Chrome, 1920x1080",
        "tags": [
          "Sandboxes"
        ]
      }
    },
    "/v1/sandboxes/{name}/command-history": {
      "get": {
        "operationId": "listCommands",
        "parameters": [
          {
            "in": "path",
            "name": "name",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "commands (default) | process | sync | stream | detached",
            "in": "query",
            "name": "mode",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "1-200, default 50",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "next_cursor of the previous page",
            "in": "query",
            "name": "cursor",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CommandList"
                }
              }
            },
            "description": "OK"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "Commands run in the sandbox, newest first (90 days, also while stopped)",
        "tags": [
          "Commands"
        ]
      }
    },
    "/v1/sandboxes/{name}/command-history/{id}": {
      "get": {
        "operationId": "getCommand",
        "parameters": [
          {
            "in": "path",
            "name": "name",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CommandRecord"
                }
              }
            },
            "description": "OK"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "One command's record",
        "tags": [
          "Commands"
        ]
      }
    },
    "/v1/sandboxes/{name}/command-history/{id}/logs": {
      "get": {
        "operationId": "readCommandLogs",
        "parameters": [
          {
            "in": "path",
            "name": "name",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "stdout (default) | stderr",
            "in": "query",
            "name": "stream",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "byte offset",
            "in": "query",
            "name": "offset",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "utf8 (default) | base64",
            "in": "query",
            "name": "encoding",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CommandLogs"
                }
              }
            },
            "description": "OK"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "A command's stored output from an offset (at most 1 MiB)",
        "tags": [
          "Commands"
        ]
      }
    },
    "/v1/sandboxes/{name}/desktop": {
      "post": {
        "operationId": "getDesktopLink",
        "parameters": [
          {
            "in": "path",
            "name": "name",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "dark (default) or light: the desktop's and the viewer's look",
            "in": "query",
            "name": "theme",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DesktopLink"
                }
              }
            },
            "description": "OK"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "A link to the desktop (1920x1080) in froog's viewer",
        "tags": [
          "Sandboxes"
        ]
      }
    },
    "/v1/sandboxes/{name}/exec": {
      "post": {
        "operationId": "exec",
        "parameters": [
          {
            "in": "path",
            "name": "name",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ExecRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExecResult"
                }
              },
              "application/x-ndjson": {
                "schema": {
                  "$ref": "#/components/schemas/ExecEvent"
                }
              }
            },
            "description": "OK"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "Run a command and wait for it; with stream: true, NDJSON events as output arrives",
        "tags": [
          "Commands"
        ]
      }
    },
    "/v1/sandboxes/{name}/files": {
      "delete": {
        "operationId": "deleteFile",
        "parameters": [
          {
            "in": "path",
            "name": "name",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "the file or directory",
            "in": "query",
            "name": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "true to delete a directory",
            "in": "query",
            "name": "recursive",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Done"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "Delete a file or directory",
        "tags": [
          "Files"
        ]
      },
      "get": {
        "operationId": "fileInfo",
        "parameters": [
          {
            "in": "path",
            "name": "name",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "relative to /home/user, or absolute",
            "in": "query",
            "name": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FileInfo"
                }
              }
            },
            "description": "OK"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "A file's details or a directory listing",
        "tags": [
          "Files"
        ]
      }
    },
    "/v1/sandboxes/{name}/files/archive": {
      "put": {
        "operationId": "uploadArchive",
        "parameters": [
          {
            "in": "path",
            "name": "name",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "the directory (created if missing)",
            "in": "query",
            "name": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/octet-stream": {
              "schema": {
                "format": "binary",
                "type": "string"
              }
            }
          },
          "required": true
        },
        "responses": {
          "204": {
            "description": "Done"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "Unpack a .tar.gz into a directory (raw bytes, up to 100 MiB)",
        "tags": [
          "Files"
        ]
      }
    },
    "/v1/sandboxes/{name}/files/content": {
      "get": {
        "operationId": "readFile",
        "parameters": [
          {
            "in": "path",
            "name": "name",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "the file",
            "in": "query",
            "name": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/octet-stream": {
                "schema": {
                  "format": "binary",
                  "type": "string"
                }
              }
            },
            "description": "The file"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "Download a file (raw bytes, up to 100 MiB)",
        "tags": [
          "Files"
        ]
      },
      "put": {
        "operationId": "writeFile",
        "parameters": [
          {
            "in": "path",
            "name": "name",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "the file",
            "in": "query",
            "name": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "e.g. 755",
            "in": "query",
            "name": "mode",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/octet-stream": {
              "schema": {
                "format": "binary",
                "type": "string"
              }
            }
          },
          "required": true
        },
        "responses": {
          "204": {
            "description": "Done"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "Upload a file (raw bytes, up to 100 MiB, atomic)",
        "tags": [
          "Files"
        ]
      }
    },
    "/v1/sandboxes/{name}/files/download-url": {
      "post": {
        "operationId": "createDownloadUrl",
        "parameters": [
          {
            "in": "path",
            "name": "name",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/NewFileUrl"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FileUrl"
                }
              }
            },
            "description": "OK"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "A link that downloads a file without an API key",
        "tags": [
          "Files"
        ]
      }
    },
    "/v1/sandboxes/{name}/files/find": {
      "get": {
        "operationId": "findInFiles",
        "parameters": [
          {
            "in": "path",
            "name": "name",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "text, or an extended regex with regex=true",
            "in": "query",
            "name": "pattern",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "default ~",
            "in": "query",
            "name": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "true for a regular expression",
            "in": "query",
            "name": "regex",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FileFind"
                }
              }
            },
            "description": "OK"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "Lines that contain a text",
        "tags": [
          "Files"
        ]
      }
    },
    "/v1/sandboxes/{name}/files/mkdir": {
      "post": {
        "operationId": "makeDirectory",
        "parameters": [
          {
            "in": "path",
            "name": "name",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MakeDir"
              }
            }
          },
          "required": true
        },
        "responses": {
          "204": {
            "description": "Done"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "Create a directory (and parents)",
        "tags": [
          "Files"
        ]
      }
    },
    "/v1/sandboxes/{name}/files/move": {
      "post": {
        "operationId": "moveFile",
        "parameters": [
          {
            "in": "path",
            "name": "name",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MoveFile"
              }
            }
          },
          "required": true
        },
        "responses": {
          "204": {
            "description": "Done"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "Move or rename",
        "tags": [
          "Files"
        ]
      }
    },
    "/v1/sandboxes/{name}/files/permissions": {
      "post": {
        "operationId": "setFilePermissions",
        "parameters": [
          {
            "in": "path",
            "name": "name",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FilePermissions"
              }
            }
          },
          "required": true
        },
        "responses": {
          "204": {
            "description": "Done"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "Change a file's or directory's permissions (chmod)",
        "tags": [
          "Files"
        ]
      }
    },
    "/v1/sandboxes/{name}/files/replace": {
      "post": {
        "operationId": "replaceInFiles",
        "parameters": [
          {
            "in": "path",
            "name": "name",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FileReplace"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "items": {
                    "$ref": "#/components/schemas/FileReplaced"
                  },
                  "type": "array"
                }
              }
            },
            "description": "OK"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "Replace a text in files, byte for byte",
        "tags": [
          "Files"
        ]
      }
    },
    "/v1/sandboxes/{name}/files/search": {
      "get": {
        "operationId": "searchFiles",
        "parameters": [
          {
            "in": "path",
            "name": "name",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "a glob, e.g. *.py",
            "in": "query",
            "name": "pattern",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "where to look, default ~",
            "in": "query",
            "name": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FileSearch"
                }
              }
            },
            "description": "OK"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "Files whose name matches a glob",
        "tags": [
          "Files"
        ]
      }
    },
    "/v1/sandboxes/{name}/files/upload-url": {
      "post": {
        "operationId": "createUploadUrl",
        "parameters": [
          {
            "in": "path",
            "name": "name",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/NewFileUrl"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FileUrl"
                }
              }
            },
            "description": "OK"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "A link that takes a PUT of a file without an API key",
        "tags": [
          "Files"
        ]
      }
    },
    "/v1/sandboxes/{name}/fork": {
      "post": {
        "operationId": "forkSandbox",
        "parameters": [
          {
            "in": "path",
            "name": "name",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Any unique string (up to 255 characters). A retry with the same key within 24 hours returns the first response instead of creating more sandboxes; the same key with a different request is a 422 idempotency_key_reused.",
            "in": "header",
            "name": "Idempotency-Key",
            "required": false,
            "schema": {
              "maxLength": 255,
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ForkSandbox"
              }
            }
          },
          "required": true
        },
        "responses": {
          "202": {
            "content": {
              "application/json": {
                "schema": {
                  "items": {
                    "$ref": "#/components/schemas/Sandbox"
                  },
                  "type": "array"
                }
              }
            },
            "description": "OK"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "New sandboxes from a snapshot of this one",
        "tags": [
          "Sandboxes"
        ]
      }
    },
    "/v1/sandboxes/{name}/jobs": {
      "get": {
        "operationId": "listJobs",
        "parameters": [
          {
            "in": "path",
            "name": "name",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "items": {
                    "$ref": "#/components/schemas/Job"
                  },
                  "type": "array"
                }
              }
            },
            "description": "OK"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "Background commands",
        "tags": [
          "Commands"
        ]
      },
      "post": {
        "operationId": "startJob",
        "parameters": [
          {
            "in": "path",
            "name": "name",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/NewJob"
              }
            }
          },
          "required": true
        },
        "responses": {
          "202": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Job"
                }
              }
            },
            "description": "OK"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "Start a background command",
        "tags": [
          "Commands"
        ]
      }
    },
    "/v1/sandboxes/{name}/jobs/{id}": {
      "get": {
        "operationId": "readJob",
        "parameters": [
          {
            "in": "path",
            "name": "name",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "byte offset to read from",
            "in": "query",
            "name": "offset",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "bytes to read",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/JobOutput"
                }
              }
            },
            "description": "OK"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "A background command's state and output from an offset",
        "tags": [
          "Commands"
        ]
      }
    },
    "/v1/sandboxes/{name}/jobs/{id}/stop": {
      "post": {
        "operationId": "stopJob",
        "parameters": [
          {
            "in": "path",
            "name": "name",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Done"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "Stop a background command",
        "tags": [
          "Commands"
        ]
      }
    },
    "/v1/sandboxes/{name}/metrics": {
      "get": {
        "operationId": "getSandboxMetrics",
        "parameters": [
          {
            "in": "path",
            "name": "name",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SandboxMetrics"
                }
              }
            },
            "description": "OK"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "CPU, memory, disk and network of a running sandbox, read now",
        "tags": [
          "Sandboxes"
        ]
      }
    },
    "/v1/sandboxes/{name}/ports": {
      "get": {
        "operationId": "listPorts",
        "parameters": [
          {
            "in": "path",
            "name": "name",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ports"
                }
              }
            },
            "description": "OK"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "Inbound ports open on the sandbox's public IPv6 address (besides SSH), including its public URLs' ports",
        "tags": [
          "Ports"
        ]
      },
      "post": {
        "operationId": "openPort",
        "parameters": [
          {
            "in": "path",
            "name": "name",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OpenPort"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ports"
                }
              }
            },
            "description": "OK"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "Open an inbound TCP or UDP port on the sandbox's IPv6 address",
        "tags": [
          "Ports"
        ]
      },
      "put": {
        "operationId": "setPorts",
        "parameters": [
          {
            "in": "path",
            "name": "name",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SetPorts"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ports"
                }
              }
            },
            "description": "OK"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "Set the ports opened on the sandbox's IPv6 address, replacing the ones opened before (kept across stops and starts)",
        "tags": [
          "Ports"
        ]
      }
    },
    "/v1/sandboxes/{name}/ports/{port}/{protocol}": {
      "delete": {
        "operationId": "closePort",
        "parameters": [
          {
            "in": "path",
            "name": "name",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "port",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "protocol",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ports"
                }
              }
            },
            "description": "OK"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "Close a port opened on the sandbox's IPv6 address",
        "tags": [
          "Ports"
        ]
      }
    },
    "/v1/sandboxes/{name}/processes": {
      "get": {
        "operationId": "listProcesses",
        "parameters": [
          {
            "in": "path",
            "name": "name",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProcessList"
                }
              }
            },
            "description": "OK"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "Processes, busiest first (a stopped sandbox: the sample taken at stop)",
        "tags": [
          "Commands"
        ]
      }
    },
    "/v1/sandboxes/{name}/prompt": {
      "post": {
        "operationId": "prompt",
        "parameters": [
          {
            "in": "path",
            "name": "name",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PromptRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExecResult"
                }
              }
            },
            "description": "OK"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "Run a coding agent to completion",
        "tags": [
          "Commands"
        ]
      }
    },
    "/v1/sandboxes/{name}/restore": {
      "post": {
        "operationId": "restoreSandbox",
        "parameters": [
          {
            "in": "path",
            "name": "name",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Restore"
              }
            }
          },
          "required": true
        },
        "responses": {
          "202": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Sandbox"
                }
              }
            },
            "description": "OK"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "Roll the sandbox back to a snapshot",
        "tags": [
          "Sandboxes"
        ]
      }
    },
    "/v1/sandboxes/{name}/snapshots": {
      "delete": {
        "operationId": "deleteAutoSnapshots",
        "parameters": [
          {
            "in": "path",
            "name": "name",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeletedSnapshots"
                }
              }
            },
            "description": "OK"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "Delete every automatic snapshot (saved ones and the one a stopped sandbox starts from stay)",
        "tags": [
          "Snapshots"
        ]
      },
      "get": {
        "operationId": "listSnapshots",
        "parameters": [
          {
            "in": "path",
            "name": "name",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "items": {
                    "$ref": "#/components/schemas/Snapshot"
                  },
                  "type": "array"
                }
              }
            },
            "description": "OK"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "A sandbox's snapshots",
        "tags": [
          "Snapshots"
        ]
      },
      "post": {
        "operationId": "saveSnapshot",
        "parameters": [
          {
            "in": "path",
            "name": "name",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateSnapshot"
              }
            }
          },
          "required": true
        },
        "responses": {
          "202": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Snapshot"
                }
              }
            },
            "description": "OK"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "Save a snapshot (with a label: an account template)",
        "tags": [
          "Snapshots"
        ]
      }
    },
    "/v1/sandboxes/{name}/snapshots/{snap}": {
      "delete": {
        "operationId": "deleteSnapshot",
        "parameters": [
          {
            "in": "path",
            "name": "name",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "snap",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Done"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "Delete a snapshot",
        "tags": [
          "Snapshots"
        ]
      },
      "get": {
        "operationId": "getSnapshot",
        "parameters": [
          {
            "in": "path",
            "name": "name",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "snap",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Snapshot"
                }
              }
            },
            "description": "OK"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "One snapshot by id or label; latest is the newest available one",
        "tags": [
          "Snapshots"
        ]
      }
    },
    "/v1/sandboxes/{name}/ssh": {
      "get": {
        "operationId": "getSshInfo",
        "parameters": [
          {
            "in": "path",
            "name": "name",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SshInfo"
                }
              }
            },
            "description": "OK"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "Where and how to SSH in (host, port, pinned host key)",
        "tags": [
          "Sandboxes"
        ]
      }
    },
    "/v1/sandboxes/{name}/start": {
      "post": {
        "operationId": "startSandbox",
        "parameters": [
          {
            "in": "path",
            "name": "name",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/StartSandbox"
              }
            }
          },
          "required": true
        },
        "responses": {
          "202": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Sandbox"
                }
              }
            },
            "description": "OK"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "Start a stopped sandbox (its auto-stop timer starts again), optionally with a new size, auto-stop timer, environment or env vars",
        "tags": [
          "Sandboxes"
        ]
      }
    },
    "/v1/sandboxes/{name}/stop": {
      "post": {
        "operationId": "stopSandbox",
        "parameters": [
          {
            "in": "path",
            "name": "name",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "202": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Sandbox"
                }
              }
            },
            "description": "OK"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "Stop: snapshot the disk, stop billing (the answer's stop_id tracks it)",
        "tags": [
          "Sandboxes"
        ]
      }
    },
    "/v1/sandboxes/{name}/touch": {
      "post": {
        "operationId": "touchSandbox",
        "parameters": [
          {
            "in": "path",
            "name": "name",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Done"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "Record activity (last_active_at); the auto-stop deadline doesn't move (PATCH ttl_secs does)",
        "tags": [
          "Sandboxes"
        ]
      }
    },
    "/v1/sandboxes/{name}/urls": {
      "get": {
        "operationId": "listUrls",
        "parameters": [
          {
            "in": "path",
            "name": "name",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "items": {
                    "$ref": "#/components/schemas/PublicUrl"
                  },
                  "type": "array"
                }
              }
            },
            "description": "OK"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "Public URLs of the sandbox's ports",
        "tags": [
          "Public URLs"
        ]
      },
      "post": {
        "operationId": "hostPort",
        "parameters": [
          {
            "in": "path",
            "name": "name",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/NewPublicUrl"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicUrl"
                }
              }
            },
            "description": "OK"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "Host a port (private with a token unless public), or rename or title it",
        "tags": [
          "Public URLs"
        ]
      }
    },
    "/v1/sandboxes/{name}/urls/{port}": {
      "delete": {
        "operationId": "removeUrl",
        "parameters": [
          {
            "in": "path",
            "name": "name",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "port",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Done"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "Remove a public URL",
        "tags": [
          "Public URLs"
        ]
      }
    },
    "/v1/sandboxes/{name}/urls/{port}/tokens": {
      "get": {
        "operationId": "listUrlTokens",
        "parameters": [
          {
            "in": "path",
            "name": "name",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "port",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "items": {
                    "$ref": "#/components/schemas/UrlToken"
                  },
                  "type": "array"
                }
              }
            },
            "description": "OK"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "Live viewer tokens of a private URL (secrets are never returned)",
        "tags": [
          "Public URLs"
        ]
      },
      "post": {
        "operationId": "createUrlToken",
        "parameters": [
          {
            "in": "path",
            "name": "name",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "port",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/NewUrlToken"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UrlToken"
                }
              }
            },
            "description": "OK"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "Another token for a private URL that expires and can be revoked on its own (the secret is returned once)",
        "tags": [
          "Public URLs"
        ]
      }
    },
    "/v1/sandboxes/{name}/urls/{port}/tokens/{id}": {
      "delete": {
        "operationId": "revokeUrlToken",
        "parameters": [
          {
            "in": "path",
            "name": "name",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "port",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Done"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "Revoke a viewer token",
        "tags": [
          "Public URLs"
        ]
      }
    },
    "/v1/sandboxes/{name}/usage": {
      "get": {
        "operationId": "getSandboxUsage",
        "parameters": [
          {
            "in": "path",
            "name": "name",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "unix seconds; default the start of the current billing period",
            "in": "query",
            "name": "since",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "unix seconds; default now",
            "in": "query",
            "name": "until",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SandboxUsage"
                }
              }
            },
            "description": "OK"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "One sandbox's running time and cost in a window, at list prices",
        "tags": [
          "Sandboxes"
        ]
      }
    },
    "/v1/sandboxes/{name}/wait": {
      "post": {
        "operationId": "waitForPort",
        "parameters": [
          {
            "in": "path",
            "name": "name",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WaitForPort"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PortWait"
                }
              }
            },
            "description": "OK"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "Wait until something listens on a TCP port inside the sandbox (ready: false after the timeout)",
        "tags": [
          "Sandboxes"
        ]
      }
    },
    "/v1/saved-snapshots": {
      "get": {
        "operationId": "listSavedSnapshots",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "items": {
                    "$ref": "#/components/schemas/SavedSnapshot"
                  },
                  "type": "array"
                }
              }
            },
            "description": "OK"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "Saved snapshots of the account (templates for from)",
        "tags": [
          "Snapshots"
        ]
      }
    },
    "/v1/saved-snapshots/{name}": {
      "delete": {
        "operationId": "deleteSavedSnapshot",
        "parameters": [
          {
            "in": "path",
            "name": "name",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Done"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "Remove a saved snapshot",
        "tags": [
          "Snapshots"
        ]
      },
      "get": {
        "operationId": "getSavedSnapshot",
        "parameters": [
          {
            "in": "path",
            "name": "name",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SavedSnapshot"
                }
              }
            },
            "description": "OK"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "One saved snapshot and its status",
        "tags": [
          "Snapshots"
        ]
      }
    },
    "/v1/snapshots": {
      "get": {
        "operationId": "listAccountSnapshots",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "items": {
                    "$ref": "#/components/schemas/Snapshot"
                  },
                  "type": "array"
                }
              }
            },
            "description": "OK"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "Every snapshot of the account, newest first, with its sandbox",
        "tags": [
          "Snapshots"
        ]
      }
    },
    "/v1/ssh-keys": {
      "get": {
        "operationId": "listSshKeys",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "items": {
                    "$ref": "#/components/schemas/SshKey"
                  },
                  "type": "array"
                }
              }
            },
            "description": "OK"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "SSH public keys that can log in to your sandboxes",
        "tags": [
          "Keys"
        ]
      },
      "post": {
        "operationId": "addSshKey",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AddSshKey"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SshKey"
                }
              }
            },
            "description": "OK"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "Add an SSH public key",
        "tags": [
          "Keys"
        ]
      }
    },
    "/v1/ssh-keys/{id}": {
      "delete": {
        "operationId": "removeSshKey",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Done"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "Remove an SSH key",
        "tags": [
          "Keys"
        ]
      }
    },
    "/v1/status": {
      "get": {
        "operationId": "getStatus",
        "responses": {
          "200": {
            "description": "Done"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "security": [],
        "summary": "Platform status and 90-day uptime",
        "tags": [
          "Account"
        ]
      }
    },
    "/v1/stops/{id}": {
      "get": {
        "operationId": "getStop",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Stop"
                }
              }
            },
            "description": "OK"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "A stop's progress: pending, completed or failed",
        "tags": [
          "Sandboxes"
        ]
      }
    },
    "/v1/telemetry": {
      "delete": {
        "operationId": "removeTelemetry",
        "responses": {
          "204": {
            "description": "Done"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "Stop the OpenTelemetry export",
        "tags": [
          "Integrations"
        ]
      },
      "get": {
        "operationId": "getTelemetry",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "anyOf": [
                    {
                      "$ref": "#/components/schemas/Telemetry"
                    },
                    {
                      "type": "null"
                    }
                  ]
                }
              }
            },
            "description": "OK"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "The OpenTelemetry endpoint (null when none)",
        "tags": [
          "Integrations"
        ]
      },
      "put": {
        "operationId": "setTelemetry",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SetTelemetry"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TelemetrySaved"
                }
              }
            },
            "description": "OK"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "Set the OpenTelemetry endpoint; sends a test export",
        "tags": [
          "Integrations"
        ]
      }
    },
    "/v1/usage": {
      "get": {
        "operationId": "getUsage",
        "parameters": [
          {
            "description": "YYYY-MM: that calendar month at list prices; default the current billing period",
            "in": "query",
            "name": "month",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Usage"
                }
              }
            },
            "description": "OK"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "Usage this billing period: per-second running time, snapshots, what the plan covers",
        "tags": [
          "Account"
        ]
      }
    },
    "/v1/waitlist": {
      "post": {
        "operationId": "joinWaitlist",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Waitlist"
              }
            }
          },
          "required": true
        },
        "responses": {
          "204": {
            "description": "Done"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "security": [],
        "summary": "Join the waitlist",
        "tags": [
          "Account"
        ]
      }
    },
    "/v1/webhooks": {
      "get": {
        "operationId": "listWebhooks",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "items": {
                    "$ref": "#/components/schemas/Webhook"
                  },
                  "type": "array"
                }
              }
            },
            "description": "OK"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "Webhooks",
        "tags": [
          "Integrations"
        ]
      },
      "post": {
        "operationId": "addWebhook",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/NewWebhook"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookSecret"
                }
              }
            },
            "description": "OK"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "Add a webhook (the signing secret is returned once)",
        "tags": [
          "Integrations"
        ]
      }
    },
    "/v1/webhooks/{id}": {
      "delete": {
        "operationId": "removeWebhook",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Done"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "Remove a webhook",
        "tags": [
          "Integrations"
        ]
      },
      "get": {
        "operationId": "getWebhook",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Webhook"
                }
              }
            },
            "description": "OK"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "One webhook",
        "tags": [
          "Integrations"
        ]
      },
      "patch": {
        "operationId": "updateWebhook",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateWebhook"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Webhook"
                }
              }
            },
            "description": "OK"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "Change a webhook's URL, events, name, or pause it (the secret stays)",
        "tags": [
          "Integrations"
        ]
      }
    },
    "/v1/webhooks/{id}/deliveries": {
      "get": {
        "operationId": "listWebhookDeliveries",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "1-200, default 50",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "items": {
                    "$ref": "#/components/schemas/WebhookDelivery"
                  },
                  "type": "array"
                }
              }
            },
            "description": "OK"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "Delivery attempts of the last 30 days, newest first",
        "tags": [
          "Integrations"
        ]
      }
    },
    "/v1/webhooks/{id}/rotate": {
      "post": {
        "operationId": "rotateWebhook",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookSecret"
                }
              }
            },
            "description": "OK"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "A new signing secret",
        "tags": [
          "Integrations"
        ]
      }
    },
    "/v1/webhooks/{id}/test": {
      "post": {
        "operationId": "testWebhook",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Done"
          },
          "default": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            },
            "description": "Error: {\"error\": {\"code\", \"message\", \"request_id\"}}"
          }
        },
        "summary": "Send a test event now",
        "tags": [
          "Integrations"
        ]
      }
    }
  },
  "security": [
    {
      "bearer": []
    }
  ],
  "servers": [
    {
      "url": "https://api.froog.tech"
    }
  ]
}
