openapi: 3.1.1
info:
  title: BoxRouter API
  version: 0.10.281
  description: |
    Hosted sandbox control using a BoxRouter API key. Provider credentials are
    configured separately and never included in workload requests. Same-origin browser
    sessions are also accepted. Browser mutations require the configured Origin and
    the session-bound X-CSRF-Token; after an organization switch, every cookie-authenticated
    mutation must also send X-BoxRouter-Selection-Version with the version read from /auth/me.
    A stale version returns 409 organization_changed before side effects; its details contain
    the current selection_version. Bearer keys do not require browser CSRF or selection headers. Core adapters
    execute on BoxRouter servers.
    Sandbox IDs are BoxRouter resource IDs; every access is scoped to the caller's
    workspace. Routing occurs only at creation. Subsequent operations retain the
    original provider, upstream account and billing mode.
    Mutation endpoints accept an Idempotency-Key. Clients should generate one per
    logical mutation and retain it for reconciliation. The server records workspace,
    method, path and request identity before dispatch. Buffered requests include the full
    body hash. Streaming uploads reserve the key first, lock concurrent dispatch, and
    finalize the body hash as bytes arrive; interrupted uploads remain unresolved and
    must not be redispatched. A replay must validate the complete body before returning
    a recorded success. Reusing a key with different input
    returns 409; identical input returns the recorded result or an unresolved-operation
    error without dispatching again. Records with unknown outcomes are retained until
    resolved; completed records remain available for at least 24 hours. This does not
    promise exactly-once upstream execution or authorize automatic mutation retries.

    ## Guarantees

    Each operation's description states what you can rely on, what you can set and what is not promised. These rules apply to all operations:

    - File operations may act with more authority than the command user: do not rely on file permissions to keep the file API out. Commands run as the command user.
    - Some features need capabilities. A request that names them runs only on infrastructure that supports them, and an operation the sandbox was not created for returns a clear error; behaviour never changes silently.
servers:
  - url: https://api.boxrouter.ai/api/v1
security:
  - BoxRouterKey: []
  - BrowserSession: []
tags:
  - name: agents
    description: Discover launch manifests available to this workspace.
  - name: identity
    description: Browser sign-in and self-service API-key administration.
  - name: organizations
    description: Browser-only team organizations, invitations and membership administration.
  - name: launches
    description: Create an interactive sandbox from an agent manifest.
  - name: launch-runs
    description: Reserve, list, observe, and stop unattended multi-copy launch runs and their copies.
  - name: sandboxes
    description: Create and manage owned sandbox resources.
  - name: commands
    description: Execute Bash commands in an existing sandbox.
  - name: files
    description: Transfer exact file bytes within an existing sandbox.
  - name: providers
    description: Discover the caller's eligible supply and capabilities.
  - name: operations
    description: Reconcile uncertain mutations without replaying them.
  - name: provider-accounts
    description: Manage encrypted hosted BYOK connections separately from workload requests.
  - name: credits
    description: Prepaid credit balance, purchases and saved payment methods; never required for BYOK work.
  - name: spend-limits
    description: The cap on the managed compute a workspace can spend; BYOK work never counts.
  - name: processes
    description: Background commands with streamed output and completion on every provider; stdin where supported.
  - name: previews
    description: URLs and enforced credentials for sandbox ports on every provider.
  - name: volumes
    description: "BoxRouter Volumes: one folder with checkpoints, restorable on any provider"
  - name: terminals
    description: Real PTY sessions on every provider through a guest helper.
  - name: secrets
    description: Manage workspace-scoped secret values without returning them.
  - name: artifacts
    description: Temporary workspace-owned inputs for provider builds and file transfers.
  - name: pricing
    description: The providers' published prices, as dated versions anyone can read without a key.
  - name: github
    description: The shared GitHub App connection used by Deployments and Actions; installations remain workspace-scoped.
  - name: actions
    description: GitHub Actions settings, policy previews and workspace-owned run, job and placement projections.
  - name: routing
    description: Workspace-wide routing authority, per-provider funding order and availability preview.
  - name: images
    description: Prepare and remove workspace-scoped provider copies of images.
paths:
  /agents:
    get:
      tags:
        - agents
      operationId: listAgents
      x-speakeasy-group: agents
      x-speakeasy-name-override: list
      summary: List launch agents
      description: |
        List the launch manifests available to this workspace. The response includes
        names, display names, descriptions, declared secret names and preview ports.
        It never includes commands, file contents or secret values.
      responses:
        "200":
          description: Available launch agents.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AgentPage"
        "401":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /agents/{name}:
    parameters:
      - name: name
        in: path
        required: true
        description: Launch agent name.
        schema:
          type: string
          minLength: 1
          maxLength: 63
          pattern: ^[a-z][a-z0-9-]{0,62}$
    get:
      tags:
        - agents
      operationId: getAgent
      x-speakeasy-group: agents
      x-speakeasy-name-override: get
      summary: Get a launch agent manifest
      description: Returns a workspace-available manifest. Unknown names return agent_not_found.
      responses:
        "200":
          description: Agent metadata and its launch manifest.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AgentDetails"
        "404":
          description: No available agent has this name.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AgentNotFoundErrorResponse"
        default:
          $ref: "#/components/responses/Error"
  /launches:
    post:
      tags:
        - launches
      operationId: createLaunch
      x-speakeasy-group: launches
      x-speakeasy-name-override: create
      summary: Create an agent launch
      description: |
        Create one workspace-owned sandbox and prepare it from a catalog agent or inline
        manifest. `overrides.lifetime_seconds` requests a lifetime in seconds in place of
        the manifest value; null there or in the manifest asks for a sandbox with no lifetime.
        The required Idempotency-Key identifies one logical launch; retry an
        uncertain request with the same key and body. BoxRouter validates the manifest,
        the secrets, the routes and the funding first and answers 400, 402 or 409 with outcome
        `not_started` for a launch it cannot start; it returns 202 only after the launch
        operation is durably accepted. Poll the operation URL from Location for
        queued/running status and phase progress. A matching key returns the same operation,
        including the original result. New requests and matching retries both return HTTP 202.
        A key used with different input returns 409 and does not start another launch.
        Setup, file, background and preview preparation failures destroy the newly created
        sandbox before the operation becomes failed.

        Secret values supplied in secret_values are used only for this request and are
        never stored or returned. Names declared by the manifest can instead resolve from
        the workspace secret store. The successful operation contains the sandbox ID,
        phase results, preview URLs and enforced expiry in result.
      parameters:
        - $ref: "#/components/parameters/LaunchIdempotencyKey"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/LaunchRequest"
            example:
              manifest:
                name: hello-agent
                command: bash
                setup:
                  - echo ready > /tmp/ready.txt
      responses:
        "202":
          description: Launch operation accepted, or recovered for this idempotency key.
          headers:
            Location:
              required: true
              description: URL to poll for launch status and preparation phase progress.
              schema:
                type: string
                format: uri-reference
            Retry-After:
              description: Minimum delay in seconds before polling the operation URL.
              schema:
                type: integer
                minimum: 1
            X-Request-ID:
              $ref: "#/components/headers/RequestId"
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/LaunchOperation"
        "400":
          description: Request validation failed or one or more named secrets are missing.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/LaunchInputErrorResponse"
        "401":
          $ref: "#/components/responses/Error"
        "402":
          description: Credit is insufficient or a configured managed spend limit would be exceeded. No provider create or hold is committed.
          headers:
            X-Request-ID:
              $ref: "#/components/headers/RequestId"
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiErrorResponse"
        "403":
          $ref: "#/components/responses/Error"
        "404":
          description: The requested catalog agent does not exist.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AgentNotFoundErrorResponse"
        "409":
          description: The idempotency key conflicts or no route can satisfy the launch before the operation is accepted. Preparation phase failures are returned on the accepted operation when polled.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/LaunchConflictErrorResponse"
        "422":
          $ref: "#/components/responses/Error"
        "429":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /launches/{operation_id}:
    parameters:
      - name: operation_id
        in: path
        required: true
        description: Workspace-owned launch operation returned by create.
        schema:
          type: string
          minLength: 1
    get:
      tags:
        - launches
      operationId: getLaunchOperation
      x-speakeasy-group: launches
      x-speakeasy-name-override: get_operation
      summary: Get launch status and phase progress
      description: |
        Returns the durable status of one launch operation owned by the caller's workspace.
        Poll while status is queued or running, respecting Retry-After when present. A
        succeeded operation includes result; a failed operation includes the original
        structured API error and its HTTP status. Secret values are never included.
      responses:
        "200":
          description: Current launch operation status.
          headers:
            Retry-After:
              description: Minimum delay in seconds before polling again while non-terminal.
              schema:
                type: integer
                minimum: 1
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/LaunchOperation"
        "401":
          $ref: "#/components/responses/Error"
        "403":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        "503":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /launches/{operation_id}/settings:
    parameters:
      - name: operation_id
        in: path
        required: true
        description: Workspace-owned launch operation returned by create.
        schema:
          type: string
          minLength: 1
    get:
      tags:
        - launches
      operationId: getLaunchSettings
      x-speakeasy-group: launches
      x-speakeasy-name-override: get_settings
      summary: Get what a launch asked for
      description: |
        Returns the choices one launch was created with: the catalog agent or the caller's own
        source, whether BoxRouter picked the provider or the caller chose one, and the names of
        the secrets it used. Every client that creates a launch (the dashboard, the CLI, the
        SDKs) is recorded the same way, so any of them can show or repeat it. Secret values are
        never recorded or returned. A launch created before settings were recorded is 404.
      responses:
        "200":
          description: The launch's recorded settings.
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/LaunchSettings"
        "401":
          $ref: "#/components/responses/Error"
        "403":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /launches/{operation_id}/command:
    parameters:
      - name: operation_id
        in: path
        required: true
        description: Workspace-owned launch operation returned by create.
        schema:
          type: string
          minLength: 1
    get:
      tags:
        - launches
      operationId: getLaunchCommand
      x-speakeasy-group: launches
      x-speakeasy-name-override: get_command
      summary: Get unattended command state
      description: |
        Returns the observed state of the unattended command attached to a launch owned by the
        caller's workspace. Observation waits up to five seconds. While the command is running,
        call this endpoint again to observe its state. `unknown` means the outcome could not be
        confirmed; it is never inferred from a missing sandbox or an observation failure.
        A launch with no recorded unattended command returns 404. This response never contains
        command text, environment variables, process identity, stdout, stderr or other output.
        `timed_out` with exit code 124 means the armed command timer was observed to expire; an
        ordinary command exit with code 124 remains `exited`.
      responses:
        "200":
          description: Current bounded observation of the unattended command.
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/LaunchCommand"
        "401":
          $ref: "#/components/responses/Error"
        "403":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        "503":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /launch-runs:
    get:
      tags:
        - launch-runs
      operationId: listLaunchRuns
      x-speakeasy-group: launchRuns
      x-speakeasy-name-override: list
      summary: List launch runs
      description: |
        List the authenticated workspace's launch runs, newest first, as lightweight summaries.
        Rows contain no copy items, specs, command data or outputs; read getLaunchRun for one
        run's copies. `total_copies` and `live_copies` cover every run in the workspace, not
        only the returned page. A live copy is one whose sandbox has not been confirmed absent,
        including copies that are stopping. The read uses stored metadata only and makes no
        provider calls. Responses are private to the workspace and must not be cached.

        If launch-run copies are not available to you, the read still answers 200, with `available`
        false, no runs and zero totals.

        `next_cursor` is an opaque, workspace-scoped value for the next page and is null on the
        last page. A cursor from another workspace or a malformed cursor is invalid_request.
      parameters:
        - name: limit
          in: query
          required: false
          description: Maximum number of runs in this page.
          schema:
            type: integer
            minimum: 1
            maximum: 50
            default: 20
        - name: cursor
          in: query
          required: false
          description: Opaque next_cursor from the previous page of this workspace's list.
          schema:
            type: string
            minLength: 1
            maxLength: 1024
      responses:
        "200":
          description: One page of run summaries with workspace-wide copy totals.
          headers:
            Cache-Control:
              description: The response is private and must not be cached.
              schema:
                type: string
                const: no-store
            X-Request-ID:
              $ref: "#/components/headers/RequestId"
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/LaunchRunList"
        "401":
          $ref: "#/components/responses/Error"
        "403":
          $ref: "#/components/responses/Error"
        "422":
          description: The limit or cursor is invalid.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/InvalidRequestErrorResponse"
        "500":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
    post:
      tags:
        - launch-runs
      operationId: createLaunchRun
      x-speakeasy-group: launchRuns
      x-speakeasy-name-override: create
      summary: Start an unattended launch run
      description: |
        Reserve one workspace-owned run and all requested copies atomically. The existing
        launch request fields select exactly one catalog agent or inline manifest; `copies`
        is required. Acceptance returns 202 with a Location for metadata polling and makes
        no provider create before the executor claims each copy. Reuse the same
        Idempotency-Key and identical body to recover an uncertain acceptance; a changed
        body conflicts and never creates another run.

        Credential selectors are resolved at each copy claim. Secret values and provider
        tokens are never stored. Saved secrets are re-read by name, so rotation can affect
        later copies. Inline secret_values, interactive launches, and unsupported manifest
        features are refused before reservation. The feature remains opt-in until compatible
        clients are released.
      parameters:
        - $ref: "#/components/parameters/LaunchIdempotencyKey"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/LaunchRunRequest"
            example:
              manifest:
                name: nightly-check
                command: python3 check.py
                interactive: false
              copies: 3
      responses:
        "202":
          description: Run and all copy rows were durably accepted, or recovered by idempotency key.
          headers:
            Location:
              required: true
              description: URL for provider-free metadata polling of this run.
              schema:
                type: string
                format: uri-reference
            X-Request-ID:
              $ref: "#/components/headers/RequestId"
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/LaunchRunDocument"
        "400":
          $ref: "#/components/responses/Error"
        "401":
          $ref: "#/components/responses/Error"
        "403":
          $ref: "#/components/responses/Error"
        "404":
          description: The selected agent or credential reference is not visible to this workspace.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiErrorResponse"
        "409":
          description: The key conflicts with a previous request, no eligible route can satisfy the run, or launch-run copies are not available to you (`capability_unavailable`).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/LaunchConflictErrorResponse"
        "422":
          description: The request is invalid or asks for an unsupported unattended-run feature.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/LaunchRunRequestErrorResponse"
        "429":
          $ref: "#/components/responses/Error"
        "500":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /launch-runs/{run_id}:
    get:
      tags:
        - launch-runs
      operationId: getLaunchRun
      x-speakeasy-group: launchRuns
      x-speakeasy-name-override: get
      summary: Read launch run metadata
      description: |
        Read the stored run row and copy rows only. This response excludes command data and
        output fields and makes no provider calls. `include=command` opts into bounded state
        and exit-code observations for active copies; the server uses at most eight
        observations at once and one five-second response deadline. Unknown, hung, saturated,
        or failed observations return `unknown`. Neither variant exposes command text,
        outputs, process handles, or credentials. Foreign and missing IDs have the same 404
        response.
      parameters:
        - name: run_id
          in: path
          required: true
          description: Workspace-owned launch run ID returned by createLaunchRun.
          schema:
            type: string
            minLength: 1
        - name: include
          in: query
          required: false
          description: Omit for provider-free metadata; use command for bounded command observations.
          schema:
            type: string
            enum:
              - command
      responses:
        "200":
          description: Stored metadata, with bounded command observations only when requested.
          headers:
            Cache-Control:
              description: The response is private and must not be cached.
              schema:
                type: string
                const: no-store
            X-Request-ID:
              $ref: "#/components/headers/RequestId"
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          content:
            application/json:
              schema:
                anyOf:
                  - $ref: "#/components/schemas/LaunchRunDocument"
                  - $ref: "#/components/schemas/LaunchRunWithCommand"
        "400":
          description: The include value is unsupported.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/InvalidRequestErrorResponse"
        "401":
          $ref: "#/components/responses/Error"
        "403":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        "409":
          description: Launch-run copies are not available to you (`capability_unavailable`).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CopiesUnavailableErrorResponse"
        "500":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /launch-runs/{run_id}/stop:
    post:
      tags:
        - launch-runs
      operationId: stopLaunchRun
      x-speakeasy-group: launchRuns
      x-speakeasy-name-override: stop
      summary: Stop a launch run
      description: Set the workspace-owned run's desired state to stopped and request copy cancellation. The request is idempotent; repeating stop returns the original stop timestamp. This does not imply that the provider sandbox was destroyed. Foreign and missing IDs have the same 404 response.
      parameters:
        - name: run_id
          in: path
          required: true
          description: Workspace-owned launch run ID returned by createLaunchRun.
          schema:
            type: string
            minLength: 1
      responses:
        "202":
          description: Stop was accepted or had already been accepted.
          headers:
            X-Request-ID:
              $ref: "#/components/headers/RequestId"
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/LaunchRunStopAccepted"
        "401":
          $ref: "#/components/responses/Error"
        "403":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        "409":
          description: Launch-run copies are not available to you (`capability_unavailable`).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CopiesUnavailableErrorResponse"
        "500":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /launch-runs/{run_id}/copies/{copy_index}/destroy:
    post:
      tags:
        - launch-runs
      operationId: destroyLaunchRunCopy
      x-speakeasy-group: launchRuns
      x-speakeasy-name-override: destroy_copy
      summary: Destroy one launch-run copy
      description: |
        Durably request cleanup of one copy of a workspace-owned run; the run and its other
        copies keep running. Acceptance records the request only: no provider call is made
        during the request, and `stopping` does not mean the sandbox is gone. The copy's
        status in getLaunchRun becomes `stopped` only after its absence is confirmed; a queued
        copy that was never dispatched has no sandbox and stops at once.

        The request is idempotent. Retrying it, or destroying a copy that is already stopping
        or stopped, returns the same 202 and leaves the copy's state and timestamps unchanged.
        Foreign and missing run IDs and copy indexes have the same 404 response. Responses are
        private and must not be cached.
      parameters:
        - name: run_id
          in: path
          required: true
          description: Workspace-owned launch run ID returned by createLaunchRun.
          schema:
            type: string
            minLength: 1
        - name: copy_index
          in: path
          required: true
          description: Zero-based copy index from the run's items.
          schema:
            type: integer
            minimum: 0
      responses:
        "202":
          description: The cleanup request was recorded or had already been recorded.
          headers:
            Cache-Control:
              description: The response is private and must not be cached.
              schema:
                type: string
                const: no-store
            X-Request-ID:
              $ref: "#/components/headers/RequestId"
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/LaunchRunCopyDestroyAccepted"
        "401":
          $ref: "#/components/responses/Error"
        "403":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        "409":
          description: Launch-run copies are not available to you (`capability_unavailable`).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CopiesUnavailableErrorResponse"
        "500":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /repositories/{owner}/{repo}/access:
    parameters:
      - name: owner
        in: path
        required: true
        description: GitHub owner of the repository.
        schema:
          type: string
          minLength: 1
          maxLength: 100
      - name: repo
        in: path
        required: true
        description: GitHub repository name.
        schema:
          type: string
          minLength: 1
          maxLength: 100
    get:
      tags:
        - launches
      operationId: checkRepositoryAccess
      x-speakeasy-group: launches
      x-speakeasy-name-override: check_repository_access
      summary: Check whether this workspace can read a repository
      description: |
        Answers, without launching anything, whether the caller's workspace can read the
        repository: public repositories directly, private ones through the workspace's linked
        GitHub App installation. A repository that cannot be read is a 200 with readable false
        and the refusal's code, message and fix. When readable, the answer also names the commit
        the default branch (or the requested ref) points at, the repository's top-level entries
        (directories end in a slash) and the recipe a launch would use. Results are cached per
        commit and never stored in a response cache.
      parameters:
        - name: ref
          in: query
          required: false
          description: Branch, tag or commit to check instead of the default branch.
          schema:
            type: string
            minLength: 1
            maxLength: 256
      responses:
        "200":
          description: Whether the repository is readable, with the detected recipe when it is.
          headers:
            Cache-Control:
              description: Always no-store.
              schema:
                type: string
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/RepositoryAccess"
        "400":
          $ref: "#/components/responses/Error"
        "401":
          $ref: "#/components/responses/Error"
        "403":
          $ref: "#/components/responses/Error"
        "503":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /github/installations:
    get:
      tags:
        - github
      operationId: listGithubInstallations
      x-boxrouter-authority: read
      x-speakeasy-group: github
      x-speakeasy-name-override: list_installations
      summary: List GitHub App installations linked to this workspace
      description: |
        Requires read authority in the authenticated workspace. Results include only
        installations linked to that workspace; an installation ID supplied by a caller
        is not authority. Empty workspaces return an empty page. This is a read and never
        installs, uninstalls or relinks the App.
      parameters:
        - $ref: "#/components/parameters/Cursor"
        - $ref: "#/components/parameters/Limit"
      responses:
        "200":
          description: Workspace-owned linked installations in stable ID order.
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/GitHubInstallationPage"
        "401":
          $ref: "#/components/responses/Error"
        "403":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /github/installations/{installation_id}:
    parameters:
      - $ref: "#/components/parameters/InstallationId"
    delete:
      tags:
        - github
      operationId: unlinkGithubInstallation
      x-boxrouter-authority: admin
      x-speakeasy-group: github
      x-speakeasy-name-override: unlink_installation
      summary: Unlink a GitHub App installation from this workspace
      description: |
        Requires workspace administrator authority. Browser-session calls require the CSRF
        token and configured Origin. An Idempotency-Key identifies this one unlink request;
        an identical replay returns its recorded result, a changed request under that key
        conflicts, and an unknown GitHub or storage outcome is reconciled without replay.
        Unlinking prevents new Actions work and does not uninstall the shared App from
        GitHub or cancel already accepted workflow runs.
      parameters:
        - $ref: "#/components/parameters/Csrf"
        - $ref: "#/components/parameters/IdempotencyKey"
      responses:
        "204":
          description: Installation link removed from this workspace; the GitHub App remains installed.
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
        "401":
          $ref: "#/components/responses/Error"
        "403":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        "409":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /github/repositories:
    get:
      tags:
        - github
      operationId: listGithubRepositories
      x-boxrouter-authority: read
      x-speakeasy-group: github
      x-speakeasy-name-override: list_repositories
      summary: List repositories visible through a linked installation
      description: |
        Requires read authority in the authenticated workspace. The installation must be
        linked to that workspace and its current GitHub permissions are revalidated; names
        are descriptive while stable repository IDs bind settings and work. Results contain
        no installation access credentials. Empty installations return an empty page.
      parameters:
        - $ref: "#/components/parameters/InstallationQuery"
        - $ref: "#/components/parameters/Cursor"
        - $ref: "#/components/parameters/Limit"
      responses:
        "200":
          description: Repositories currently available to this linked installation.
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/GitHubRepositoryPage"
        "401":
          $ref: "#/components/responses/Error"
        "403":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /actions/repositories:
    get:
      tags:
        - actions
      operationId: listActionsRepositories
      x-boxrouter-authority: read
      x-speakeasy-group: actions.repositories
      x-speakeasy-name-override: list
      summary: Batch Actions enablement and defaults for the given repositories
      description: |
        Requires read authority in the authenticated workspace. Returns one settings
        document per requested repository ID, ordered by repository ID: the same
        documents `getActionsRepository` serves, each with its ETag beside it instead
        of in a header, so a setup read replaces one request per repository with this
        one. A repository with no settings record appears as `exists: false`; omit
        If-Match on its first write and send its ETag for updates once it exists.
      parameters:
        - name: repository_id
          in: query
          required: true
          description: Repositories to read; repeat to select several, for example `repository_id=101&repository_id=202`.
          style: form
          explode: true
          schema:
            type: array
            minItems: 1
            maxItems: 250
            uniqueItems: true
            items:
              type: string
              minLength: 1
              maxLength: 18
              pattern: ^0*[1-9][0-9]*$
      responses:
        "200":
          description: Per-repository settings documents and their CAS tokens.
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ActionsRepositoriesResponse"
        "401":
          $ref: "#/components/responses/Error"
        "403":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /actions/repositories/{repository_id}:
    parameters:
      - $ref: "#/components/parameters/RepositoryId"
    get:
      tags:
        - actions
      operationId: getActionsRepository
      x-boxrouter-authority: read
      x-speakeasy-group: actions.repositories
      x-speakeasy-name-override: get
      summary: Get Actions enablement and defaults for a repository
      description: |
        Requires read authority in the authenticated workspace. The repository must have
        a currently linked, verified GitHub installation. The returned ETag identifies
        this settings revision. A missing settings record is represented as `exists: false`,
        disabled with no required policy; it is not silently enabled by reading it. For
        `exists: false`, omit If-Match on the first write; for `exists: true`, send the
        returned ETag to update the existing record safely.
      responses:
        "200":
          description: Workspace-owned settings and safe default state.
          headers:
            ETag:
              description: Strong settings revision; use it for writes when exists is true.
              schema:
                type: string
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ActionsRepositorySettings"
        "401":
          $ref: "#/components/responses/Error"
        "403":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
    put:
      tags:
        - actions
      operationId: updateActionsRepository
      x-boxrouter-authority: admin
      x-speakeasy-group: actions.repositories
      x-speakeasy-name-override: update
      summary: Enable or update Actions defaults for a repository
      description: |
        Requires workspace administrator authority. Browser-session calls require the CSRF
        token and configured Origin. The minimal enable request is `{"enabled":true}`;
        Actions creates its safe default settings and begins automatic image preparation
        without requiring a named policy, provider key, runner registration or YAML change.
        `policy_id` is optional and, when null, selects the workspace's current safe
        defaults. When the preceding read returned `exists: false`, omit If-Match to create
        the settings record; when it returned `exists: true`, send its ETag to update. The
        server rejects a write if that existence state changed after the read. An
        Idempotency-Key records
        the logical mutation: identical uncertain retries return the recorded outcome,
        changed input under the same key conflicts, and unknown effects are reconciled
        without automatic redispatch. Disabling stops new admission; it does not cancel
        accepted GitHub workflow runs or change their funding binding.
      parameters:
        - $ref: "#/components/parameters/CsrfBrowserOnly"
        - name: If-Match
          in: header
          required: false
          description: Required when replacing an existing settings record; omit only to create the default record.
          schema:
            type: string
            minLength: 1
            maxLength: 256
            pattern: ^[^\u0000]*$
        - $ref: "#/components/parameters/IdempotencyKey"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UpdateActionsRepository"
            examples:
              enableWithDefaults:
                summary: Enable with automatically created safe defaults
                value:
                  enabled: true
      responses:
        "200":
          description: Current settings revision; image preparation may still be in progress.
          headers:
            ETag:
              description: Strong settings revision for the next conditional update.
              schema:
                type: string
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ActionsRepositorySettings"
        "400":
          $ref: "#/components/responses/Error"
        "401":
          $ref: "#/components/responses/Error"
        "403":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        "409":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /routing/settings:
    get:
      tags:
        - routing
      operationId: getRoutingSettings
      x-boxrouter-authority: read
      x-speakeasy-group: routing
      x-speakeasy-name-override: settings
      summary: Read workspace routing and provider funding
      description: |
        Workspace read authority required. Returns each independently versioned section
        and its opaque ETag in section_etags. Never returns credentials. The GET itself
        has no whole-document validator; send a chosen section's token in If-Match to
        guard a write to that section.
        On a workspace's first read, the server atomically creates sections from its
        legacy funding state. Concurrent first reads return the same persisted sections
        and ETags. Later reads do not change settings unless a supported provider,
        managed offer or connected account changed and needs reconciliation; only a
        section that actually changes advances its ETag. The saved Prevent Overrides
        switch is retained across reads.
      responses:
        "200":
          description: Current settings and section validators.
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/RoutingSettings"
        "401":
          $ref: "#/components/responses/Error"
        "403":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
    put:
      tags:
        - routing
      operationId: updateRoutingSettings
      x-boxrouter-authority: admin
      x-speakeasy-group: routing
      x-speakeasy-name-override: updateSettings
      summary: Update one workspace routing section
      description: |
        Workspace admin (or provider-accounts:manage for provider funding) required;
        browser mutations also require Origin and X-CSRF-Token. The body names exactly
        one section: routing fields or one provider funding section. Send only the fields
        to change; every omitted field keeps its stored value, and the merged section is
        validated as a complete section before it is written. If-Match is optional. When
        sent, it must be the opaque token returned for THAT section by GET or the call
        fails 412 with no write; when omitted, the named fields are written to the
        current section. Atomically update only the named section and advance only its
        ETag. A stale token for that same section returns 412 with no write; a concurrent
        Routing edit cannot conflict with provider funding, and edits to distinct
        providers cannot conflict with one another. Reject duplicate account IDs,
        foreign/wrong-provider accounts and newly enabled Credits on unavailable managed
        providers before writing. Adding a key does not change provider exclusion.
        Identical idempotency replay returns the previously recorded response, not a
        second write.
      parameters:
        - name: If-Match
          in: header
          required: false
          description: |
            Optional opaque ETag for the exact section named in the request body. When sent,
            it must match the section's current token or the call fails 412
            routing_settings_stale. When omitted, the named fields are written to the
            current section.
          schema:
            type: string
            minLength: 1
        - $ref: "#/components/parameters/IdempotencyKey"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/RoutingSettingsUpdate"
            example:
              section: routing
              routing:
                mode: cheapest
      responses:
        "200":
          description: Current complete settings including updated section validator.
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/RoutingSettings"
        "400":
          $ref: "#/components/responses/Error"
        "401":
          $ref: "#/components/responses/Error"
        "403":
          $ref: "#/components/responses/Error"
        "409":
          $ref: "#/components/responses/Error"
        "412":
          $ref: "#/components/responses/Error"
        "422":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /routing/preview:
    get:
      tags:
        - routing
      operationId: getRoutingPreview
      x-boxrouter-authority: read
      x-speakeasy-group: routing
      x-speakeasy-name-override: preview
      summary: Preview workspace route availability without dispatch
      description: |
        Workspace read authority required. Side-effect-free current eligibility and
        versioned hold quotes from admission. No next-job prediction, workspace spend
        cap or promise that a later create can start. No secret or foreign balance.
      responses:
        "200":
          description: Learning state, availability and skipped-route reasons.
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/RoutingPreview"
        "401":
          $ref: "#/components/responses/Error"
        "403":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /actions/readiness:
    get:
      tags:
        - actions
      operationId: getActionsReadiness
      x-boxrouter-authority: read
      x-speakeasy-group: actions
      x-speakeasy-name-override: readiness
      summary: Read Actions runner and workspace funding readiness
      description: |
        Requires workspace read authority. This read-only view uses the workspace's
        existing provider accounts and credits. Unknown source facts remain unknown;
        link targets appear only when an existing page can address the reported issue.
        When `repository_id` is supplied, readiness is scoped to that repository's saved
        software profile. The profile is resolved from workspace-owned settings; callers
        cannot select a profile through this parameter.
      parameters:
        - name: repository_id
          in: query
          required: false
          description: Optional GitHub repository ID whose saved software profile should be checked.
          schema:
            type: string
            minLength: 1
            maxLength: 18
            pattern: ^0*[1-9][0-9]*$
      responses:
        "200":
          description: Current Actions readiness from workspace evidence.
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ActionsReadinessResponse"
        "401":
          $ref: "#/components/responses/Error"
        "403":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /actions/runner-catalog:
    get:
      tags:
        - actions
      operationId: getActionsRunnerCatalog
      x-boxrouter-authority: read
      x-speakeasy-group: actions
      x-speakeasy-name-override: runnerCatalog
      summary: Read the recommended Actions runner labels and resource floors
      description: |
        Requires workspace read authority. The catalog is fixed for a server release and does not
        depend on the workspace, so clients and caches may reuse it until its ETag changes. It does not assert live route availability or
        verified software; use readiness for that. Readiness still carries `runner_catalog` for
        older clients, but new clients should read this operation instead.
      responses:
        "200":
          description: The recommended labels, resource floors and default label.
          headers:
            ETag:
              description: Strong validator for this catalog; changes only when the catalog changes.
              schema:
                type: string
            Cache-Control:
              description: Caching directive; the catalog is private to the caller's session and safe to reuse until revalidated.
              schema:
                type: string
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ActionsRunnerCatalog"
        "401":
          $ref: "#/components/responses/Error"
        "403":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /actions/policies:
    get:
      tags:
        - actions
      operationId: listActionsPolicies
      x-boxrouter-authority: read
      x-speakeasy-group: actions.policies
      x-speakeasy-name-override: list
      summary: List workspace Actions policies
      description: Requires workspace read authority. Stable ID-ordered pages may be empty; listing never changes policy or starts compute.
      parameters:
        - $ref: "#/components/parameters/Cursor"
        - $ref: "#/components/parameters/Limit"
      responses:
        "200":
          description: Workspace-owned policies; an empty workspace returns an empty page.
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ActionsPolicyPage"
        "401":
          $ref: "#/components/responses/Error"
        "403":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
    post:
      tags:
        - actions
      operationId: createActionsPolicy
      x-boxrouter-authority: admin
      x-speakeasy-group: actions.policies
      x-speakeasy-name-override: create
      summary: Create a named workspace Actions policy
      description: |
        Requires workspace administrator authority. Browser-session calls require the CSRF
        token and configured Origin. Policy settings can only narrow workspace and
        repository guardrails. The workspace funding choice decides the logical authority
        (workspace BYOK or platform managed); the actual provider, account and payer binding
        is confirmed separately for each attempt and is never taken from a policy. A policy
        cannot change supply or add accounts. Idempotency-Key replays the recorded
        outcome for identical input; changed input conflicts. An unknown outcome is
        reconciled without creating a second policy.
      parameters:
        - $ref: "#/components/parameters/CsrfBrowserOnly"
        - $ref: "#/components/parameters/IdempotencyKey"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateActionsPolicy"
            example:
              name: fast-builds
              settings:
                profile: linux-basic
                max_runtime_seconds: 900
      responses:
        "201":
          description: Created workspace policy and revision.
          headers:
            ETag:
              description: Strong policy revision for conditional updates.
              schema:
                type: string
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ActionsPolicy"
        "400":
          $ref: "#/components/responses/Error"
        "401":
          $ref: "#/components/responses/Error"
        "403":
          $ref: "#/components/responses/Error"
        "409":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /actions/policies/{policy_id}:
    parameters:
      - $ref: "#/components/parameters/PolicyId"
    get:
      tags:
        - actions
      operationId: getActionsPolicy
      x-boxrouter-authority: read
      x-speakeasy-group: actions.policies
      x-speakeasy-name-override: get
      summary: Get a workspace Actions policy
      description: Requires workspace read authority. The policy ID is workspace-scoped; foreign and unknown IDs both return 404.
      responses:
        "200":
          description: Policy definition and current revision.
          headers:
            ETag:
              description: Strong policy revision for conditional updates.
              schema:
                type: string
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ActionsPolicy"
        "401":
          $ref: "#/components/responses/Error"
        "403":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
    put:
      tags:
        - actions
      operationId: updateActionsPolicy
      x-boxrouter-authority: admin
      x-speakeasy-group: actions.policies
      x-speakeasy-name-override: update
      summary: Replace a workspace Actions policy
      description: |
        Requires workspace administrator authority. Browser-session calls require CSRF and
        the configured Origin. If-Match is required and must equal the latest ETag. Policy
        changes only narrow workspace/repository guardrails and do not mutate accepted jobs
        or change their supply, accounts or logical funding authority. Idempotency-Key replays an identical recorded update;
        changed input conflicts. A stale ETag returns 409 without writing. Unknown outcomes
        are reconciled, never automatically redispatched.
      parameters:
        - $ref: "#/components/parameters/CsrfBrowserOnly"
        - $ref: "#/components/parameters/IfMatch"
        - $ref: "#/components/parameters/IdempotencyKey"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UpdateActionsPolicy"
            example:
              name: fast-builds
              settings:
                profile: linux-basic
                max_runtime_seconds: 1200
      responses:
        "200":
          description: Updated policy revision.
          headers:
            ETag:
              description: Strong policy revision for the next conditional update.
              schema:
                type: string
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ActionsPolicy"
        "400":
          $ref: "#/components/responses/Error"
        "401":
          $ref: "#/components/responses/Error"
        "403":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        "409":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
    delete:
      tags:
        - actions
      operationId: deleteActionsPolicy
      x-boxrouter-authority: admin
      x-speakeasy-group: actions.policies
      x-speakeasy-name-override: delete
      summary: Delete an unused workspace Actions policy
      description: |
        Requires workspace administrator authority. Browser-session calls require CSRF and
        the configured Origin. If-Match is required. A policy referenced by repository
        defaults or accepted work cannot be deleted and returns 409. Idempotency-Key
        returns the recorded delete result for identical retries; changed input conflicts. Unknown outcomes are
        reconciled without repeating the deletion.
      parameters:
        - $ref: "#/components/parameters/CsrfBrowserOnly"
        - $ref: "#/components/parameters/IfMatch"
        - $ref: "#/components/parameters/IdempotencyKey"
      responses:
        "204":
          description: Policy removed; referenced policies are refused with 409.
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
        "401":
          $ref: "#/components/responses/Error"
        "403":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        "409":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /actions/preview:
    post:
      tags:
        - actions
      operationId: previewActions
      x-boxrouter-authority: read
      x-speakeasy-group: actions
      x-speakeasy-name-override: preview
      summary: Preview Actions label and policy eligibility
      description: |
        Requires read authority for the linked repository in the authenticated workspace.
        This is a read-only evaluation of the same resolved policy and route matcher used
        for dispatch. It creates no runner, sandbox, reservation or billing hold. Refused
        and unknown results remain explicit; the preview never relaxes a requirement or
        claims an unverified route is eligible. It does not verify runner images. Runner image readiness is checked separately before dispatch.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ActionsPreviewRequest"
            example:
              repository_id: "303"
      responses:
        "200":
          description: Resolved eligibility, including an explicit refusal or unknown result.
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ActionsPreviewResult"
        "400":
          $ref: "#/components/responses/Error"
        "401":
          $ref: "#/components/responses/Error"
        "403":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /actions/runs:
    get:
      tags:
        - actions
      operationId: listActionsRuns
      x-boxrouter-authority: read
      x-speakeasy-group: actions.runs
      x-speakeasy-name-override: list
      summary: List workspace Actions workflow-run projections
      description: |
        Requires workspace read authority. Pages may be empty and default to newest-first
        by creation time; `sort` also supports oldest-first by creation time and longest
        first by duration. Equal sort values retain a stable run ID order, including
        across pages. A run's status and cost are BoxRouter's latest recorded evidence;
        this read does not poll GitHub, change a run, start a job or retry an uncertain
        effect.
      parameters:
        - name: q
          in: query
          required: false
          description: |
            Search text, case-insensitive. Matches the workflow name, repository name, branch, a
            commit SHA prefix, or a run number written as `42` or `#42`.
          schema:
            $ref: "#/components/schemas/ActionsRunFilterText"
        - name: repository_id
          in: query
          required: false
          description: Repository ID; repeat to select several (OR).
          style: form
          explode: true
          schema:
            type: array
            minItems: 1
            maxItems: 50
            uniqueItems: true
            items:
              type: string
              minLength: 1
              maxLength: 128
              pattern: ^[A-Za-z0-9_.:-]+$
        - name: state
          in: query
          required: false
          description: |
            Run state, or for an ended run its result status (`succeeded`, `failed`, `cancelled`, `skipped`, `neutral`);
            repeat to select several (OR), for example `state=running&state=queued`.
          style: form
          explode: true
          schema:
            type: array
            minItems: 1
            maxItems: 50
            uniqueItems: true
            items:
              $ref: "#/components/schemas/ActionsRunStateFilter"
        - name: workflow
          in: query
          required: false
          description: Workflow name; repeat to select several (OR). Values come from the facets.
          style: form
          explode: true
          schema:
            $ref: "#/components/schemas/ActionsRunFilterValues"
        - name: branch
          in: query
          required: false
          description: Branch name; repeat to select several (OR).
          style: form
          explode: true
          schema:
            $ref: "#/components/schemas/ActionsRunFilterValues"
        - name: triggered_by
          in: query
          required: false
          description: GitHub login of the triggering actor, bots included (`dependabot[bot]`); repeat for several (OR).
          style: form
          explode: true
          schema:
            $ref: "#/components/schemas/ActionsRunFilterValues"
        - name: job
          in: query
          required: false
          description: A job selection as unpadded base64url-encoded UTF-8 JSON `[workflow, job]`; facet labels remain `Workflow › job`. A run matches when it has any given job. Repeat for several (OR).
          style: form
          explode: true
          schema:
            $ref: "#/components/schemas/ActionsRunFilterValues"
        - name: runner_label
          in: query
          required: false
          description: A requested runner label; a run matches when any job requested any given label. Repeat for several (OR).
          style: form
          explode: true
          schema:
            $ref: "#/components/schemas/ActionsRunFilterValues"
        - name: sort
          in: query
          required: false
          description: |
            `newest` (default) or `oldest` by creation, or `longest` by duration from first start to
            end; runs without a duration (active, or end not confirmed) come last. A `next_cursor`
            continues the same filters and sort.
          schema:
            type: string
            enum:
              - newest
              - oldest
              - longest
            default: newest
        - $ref: "#/components/parameters/Cursor"
        - $ref: "#/components/parameters/Limit"
      responses:
        "200":
          description: Workspace-owned workflow runs; an empty workspace returns an empty page.
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ActionsRunPage"
        "401":
          $ref: "#/components/responses/Error"
        "403":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /actions/runs/facets:
    get:
      tags:
        - actions
      operationId: listActionsRunFacets
      x-boxrouter-authority: read
      x-speakeasy-group: actions.runs
      x-speakeasy-name-override: listFacets
      summary: Count the values each run filter offers
      description: |
        For each filter of `listActionsRuns`, its values with the number of runs in the whole
        workspace that have the value, ignoring any active filters. A run counts once for each
        value it has. Requires workspace read authority; it does not poll GitHub.
      responses:
        "200":
          description: Filter values and counts.
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ActionsRunFacets"
        "401":
          $ref: "#/components/responses/Error"
        "403":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /actions/pool-idle-time:
    get:
      tags:
        - actions
      operationId: getActionsPoolIdleTime
      x-boxrouter-authority: read
      x-speakeasy-group: actions.poolIdleTime
      x-speakeasy-name-override: get
      summary: Get the billed time of runners that served no job
      description: |
        Requires workspace read authority. A pooled runner can cost money while no GitHub job
        shows it: it sat idle until the pool drained it, or its pickup was foreign or revoked,
        or it failed after its sandbox existed. That time is charged to the payer and shown
        here, apart from every run and job. The read counts the runners that started in
        `[since, until)`, in total and per pool, with the same `billing` object a run carries.
        `idle_seconds` is each runner's lifetime from creation to its end (to now for a runner
        that is still alive). `until` defaults to now, `since` to 30 days before `until`, and the
        range may not exceed 92 days; a longer or empty range returns 400. It does not poll
        GitHub or a provider and does not change a runner.
      parameters:
        - name: since
          in: query
          required: false
          description: Start of the range, inclusive.
          schema:
            type: string
            format: date-time
        - name: until
          in: query
          required: false
          description: End of the range, exclusive.
          schema:
            type: string
            format: date-time
      responses:
        "200":
          description: Idle runner time and its billing for the range.
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ActionsPoolIdleTimeReport"
        "400":
          $ref: "#/components/responses/Error"
        "401":
          $ref: "#/components/responses/Error"
        "403":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /actions/runs/{run_id}:
    parameters:
      - $ref: "#/components/parameters/RunId"
    get:
      tags:
        - actions
      operationId: getActionsRun
      x-boxrouter-authority: read
      x-speakeasy-group: actions.runs
      x-speakeasy-name-override: get
      summary: Get a workflow-run projection and aggregate billing evidence
      description: Requires workspace read authority. Foreign and unknown run IDs both return 404; state and costs are not inferred from missing observations.
      responses:
        "200":
          description: Latest recorded workflow-run state and billing projection.
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ActionsRun"
        "401":
          $ref: "#/components/responses/Error"
        "403":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /actions/runs/{run_id}/jobs:
    parameters:
      - $ref: "#/components/parameters/RunId"
    get:
      tags:
        - actions
      operationId: listActionsRunJobs
      x-boxrouter-authority: read
      x-speakeasy-group: actions.runs
      x-speakeasy-name-override: list_jobs
      summary: List GitHub jobs projected for a workflow run
      description: Requires workspace read authority. Job assignment is tied to the runner GitHub actually assigns, not the job a JIT label initially requested.
      parameters:
        - $ref: "#/components/parameters/Cursor"
        - $ref: "#/components/parameters/Limit"
      responses:
        "200":
          description: Workspace-owned job projections; an empty run returns an empty page.
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ActionsJobPage"
        "401":
          $ref: "#/components/responses/Error"
        "403":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /actions/runs/{run_id}/placements:
    parameters:
      - $ref: "#/components/parameters/RunId"
    get:
      tags:
        - actions
      operationId: listActionsRunPlacements
      x-boxrouter-authority: read
      x-speakeasy-group: actions.runs
      x-speakeasy-name-override: list_placements
      summary: List placements attempted for a workflow run
      description: Requires workspace read authority. Placement state, outcome, cleanup and billing evidence remain separate; uncertain effects are never replayed by this read.
      parameters:
        - $ref: "#/components/parameters/Cursor"
        - $ref: "#/components/parameters/Limit"
      responses:
        "200":
          description: Workspace-owned placement history; an empty run returns an empty page.
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ActionsPlacementPage"
        "401":
          $ref: "#/components/responses/Error"
        "403":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /actions/jobs/{job_id}:
    parameters:
      - $ref: "#/components/parameters/JobId"
    get:
      tags:
        - actions
      operationId: getActionsJob
      x-boxrouter-authority: read
      x-speakeasy-group: actions.jobs
      x-speakeasy-name-override: get
      summary: Get an Actions job projection
      description: Requires workspace read authority. The GitHub assignment is authoritative; a requested job ID is not treated as runner reservation.
      responses:
        "200":
          description: Latest workspace-owned job projection and linked placement IDs.
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ActionsJob"
        "401":
          $ref: "#/components/responses/Error"
        "403":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /actions/jobs/{job_id}/steps:
    parameters:
      - $ref: "#/components/parameters/JobId"
    get:
      tags:
        - actions
      operationId: getActionsJobSteps
      x-boxrouter-authority: execute
      x-speakeasy-group: actions.jobs
      x-speakeasy-name-override: getSteps
      summary: Get GitHub workflow steps for an Actions job
      description: |
        Requires workspace execute authority. Resolves the selected workspace-owned
        job and its exact historical run attempt before reading the assigned GitHub
        workflow job. Steps preserve GitHub source order and original step numbers;
        position is the zero-based display order. Pending, unavailable, forbidden and
        removed are explicit source states. Step output is not included.
      responses:
        "200":
          description: Current GitHub workflow step metadata for the selected job.
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ActionsJobSteps"
        "400":
          $ref: "#/components/responses/Error"
        "401":
          $ref: "#/components/responses/Error"
        "403":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /actions/jobs/{job_id}/logs:
    parameters:
      - $ref: "#/components/parameters/JobId"
    get:
      tags:
        - actions
      operationId: getActionsJobLogs
      x-boxrouter-authority: execute
      x-speakeasy-group: actions.jobs
      x-speakeasy-name-override: getLogs
      summary: Read a bounded prefix of the selected GitHub job log
      description: |
        Requires workspace execute authority. Resolves the workspace-owned job,
        current repository binding, assigned GitHub job and exact historical attempt
        before fetching only that native whole-job log. At most 1 MiB is returned;
        partial indicates a bounded prefix. Output is transient and is not retained.
        Individual step log availability depends on the GitHub App/repository source.
      responses:
        "200":
          description: Selected whole-job log prefix or an explicit source availability state.
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ActionsJobLogs"
        "401":
          $ref: "#/components/responses/Error"
        "403":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /actions/jobs/{job_id}/steps/{step_position}/logs:
    parameters:
      - $ref: "#/components/parameters/JobId"
      - name: step_position
        in: path
        required: true
        description: Zero-based position in the selected fresh GitHub job's steps array.
        schema:
          type: integer
          minimum: 0
    get:
      tags:
        - actions
      operationId: getActionsJobStepLogs
      x-boxrouter-authority: execute
      x-speakeasy-group: actions.jobs
      x-speakeasy-name-override: getStepLogs
      summary: Read a bounded prefix of one GitHub step log
      description: |
        Requires workspace execute authority. step_position is zero-based and is
        verified against the selected fresh GitHub job before this endpoint is read.
        GitHub may report individual step logs unavailable for an App/repository;
        this is returned explicitly without synthesizing output from the whole-job log.
        At most 1 MiB is returned and partial indicates a bounded prefix.
      responses:
        "200":
          description: Selected step log prefix or an explicit source availability state.
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ActionsJobLogs"
        "400":
          $ref: "#/components/responses/Error"
        "401":
          $ref: "#/components/responses/Error"
        "403":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /actions/jobs/{job_id}/events:
    parameters:
      - $ref: "#/components/parameters/JobId"
    get:
      tags:
        - actions
      operationId: getActionsJobEvents
      x-boxrouter-authority: execute
      x-speakeasy-group: actions.jobs
      x-speakeasy-name-override: listEvents
      summary: List Actions step events for a job
      parameters:
        - $ref: "#/components/parameters/Cursor"
        - $ref: "#/components/parameters/Limit"
      description: |
        Requires workspace execute authority. Returns durable step events recorded for
        this workspace-owned Actions job, ordered by event time and ID. Use the
        continuation cursor to retrieve the next page; the list is empty when no
        events are available.
      responses:
        "200":
          description: Workspace-scoped page of step events for the job, in ascending event order.
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ActionsJobEventPage"
        "400":
          $ref: "#/components/responses/Error"
        "401":
          $ref: "#/components/responses/Error"
        "403":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /actions/placements/{placement_id}:
    parameters:
      - $ref: "#/components/parameters/PlacementId"
    get:
      tags:
        - actions
      operationId: getActionsPlacement
      x-boxrouter-authority: read
      x-speakeasy-group: actions.placements
      x-speakeasy-name-override: get
      summary: Get an Actions placement and its cost and cleanup evidence
      description: Requires workspace read authority. A completed workflow job does not prove sandbox cleanup or final charge; pending and unknown remain explicit.
      responses:
        "200":
          description: Latest recorded placement state, actual assignment, billing and cleanup evidence.
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ActionsPlacement"
        "401":
          $ref: "#/components/responses/Error"
        "403":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /actions/runs/{run_id}/cancel:
    parameters:
      - $ref: "#/components/parameters/RunId"
    post:
      tags:
        - actions
      operationId: cancelActionsRun
      x-boxrouter-authority: execute
      x-speakeasy-group: actions.runs
      x-speakeasy-name-override: cancel
      summary: Ask GitHub to cancel the entire workflow run
      description: |
        Requires workspace execute authority. This explicitly cancels the whole GitHub
        workflow run, including its other jobs; it cannot cancel only one job. GitHub
        remains authoritative for cancellation. A 202 means GitHub acknowledged the
        request, not that every job or sandbox has ended. Idempotency-Key identifies this
        user-authorized mutation. An identical replay returns the recorded result; changed
        input conflicts. Unknown outcomes are reconciled against GitHub without repeating
        the cancellation automatically. BoxRouter never cancels as a side effect of a
        read, timeout or cleanup failure.
      parameters:
        - $ref: "#/components/parameters/CsrfBrowserOnly"
        - $ref: "#/components/parameters/IdempotencyKey"
      responses:
        "202":
          description: Whole-workflow cancellation request accepted by GitHub; completion remains separately observed.
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ActionsRunControlResult"
        "401":
          $ref: "#/components/responses/Error"
        "403":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        "409":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /actions/runs/{run_id}/rerun:
    parameters:
      - $ref: "#/components/parameters/RunId"
    post:
      tags:
        - actions
      operationId: rerunActionsRun
      x-boxrouter-authority: execute
      x-speakeasy-group: actions.runs
      x-speakeasy-name-override: rerun
      summary: Ask GitHub to rerun the entire workflow
      description: |
        Requires workspace execute authority. This explicitly asks GitHub to rerun the
        entire workflow run, not one job or only failed jobs. GitHub creates and owns the
        new attempt. A 202 means GitHub acknowledged the request; it does not mean the new
        jobs have started. Idempotency-Key identifies this user-authorized mutation. An
        identical replay returns the recorded result; changed input conflicts. An unknown
        outcome is reconciled against GitHub and never automatically submitted again.
        Actions admission still rechecks repository binding, policy, trust and the
        immutable workspace/account/payer before creating any new placement.
      parameters:
        - $ref: "#/components/parameters/CsrfBrowserOnly"
        - $ref: "#/components/parameters/IdempotencyKey"
      responses:
        "202":
          description: Whole-workflow rerun accepted by GitHub; the next attempt is observed separately.
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ActionsRunControlResult"
        "401":
          $ref: "#/components/responses/Error"
        "403":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        "409":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /actions/runs/{run_id}/rerun-failed-jobs:
    parameters:
      - $ref: "#/components/parameters/RunId"
    post:
      tags:
        - actions
      operationId: rerunActionsRunFailedJobs
      x-boxrouter-authority: execute
      x-speakeasy-group: actions.runs
      x-speakeasy-name-override: rerunFailedJobs
      summary: Ask GitHub to rerun failed jobs and their dependents
      description: |
        Requires workspace execute authority. This explicitly asks GitHub to rerun the failed
        jobs of the entire workflow run and the jobs that depend on them in a new attempt.
        Passed jobs rerun only when they depend on a failed job. GitHub creates and owns the new attempt. A 202 means
        GitHub acknowledged the request; it does not mean the new jobs have started.
        Idempotency-Key identifies this user-authorized mutation. An identical replay returns
        the recorded result; changed input conflicts. An unknown outcome is reconciled against
        GitHub and never automatically submitted again. A run with no failed job is refused
        with 409. Actions admission still rechecks repository binding, policy, trust and the
        immutable workspace/account/payer before creating any new placement.
      parameters:
        - $ref: "#/components/parameters/CsrfBrowserOnly"
        - $ref: "#/components/parameters/IdempotencyKey"
      responses:
        "202":
          description: Failed-jobs rerun accepted by GitHub; the next attempt is observed separately.
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ActionsRunControlResult"
        "401":
          $ref: "#/components/responses/Error"
        "403":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        "409":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /operations:
    get:
      tags:
        - operations
      operationId: listOperations
      x-speakeasy-group: operations
      x-speakeasy-name-override: list
      summary: List recorded workspace operations
      description: "Stable ID-ordered pagination over authoritative mutation records. Observed time is the lookup time, not the original execution timestamp. Listing never reconciles or retries work. `sandbox_id` and `label` keep only operations whose sandbox is that ID or carries every given label, so the operations behind a labelled batch of sandboxes take one filtered list instead of paging through the workspace; a create that never made a sandbox has none to match. Unknown or repeated parameters fail the request (`label` may repeat). A request refused before anything was reserved (its error reply says `outcome: not_started` and carries `operation_id: null`) leaves no operation; its reply's `request_id`, `code` and `outcome` are the record of the attempt."
      parameters:
        - $ref: "#/components/parameters/Cursor"
        - $ref: "#/components/parameters/Limit"
        - name: sandbox_id
          in: query
          description: Only operations on this sandbox.
          schema:
            type: string
            minLength: 1
            maxLength: 128
            pattern: ^[^\u0000]*$
        - name: label
          in: query
          description: "A label the sandbox must carry: key=value matches that exact value (the value may be empty), and a bare key matches any value. Repeat for labels that must all match."
          style: form
          explode: true
          schema:
            type: array
            items:
              type: string
              pattern: ^[^=]+(=.*)?$
      responses:
        "200":
          description: Owned operation records.
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                  - next_cursor
                properties:
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/Operation"
                  next_cursor:
                    type:
                      - string
                      - "null"
        "400":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /artifacts:
    post:
      tags:
        - artifacts
      operationId: uploadArtifact
      x-speakeasy-group: artifacts
      x-speakeasy-name-override: upload
      summary: Upload a staged input
      description: |
        Stream opaque source bytes to workspace-scoped temporary storage. Enforce
        workspace upload quotas without buffering the entire request in memory.
        Compute the digest server-side; return a handle only after the full upload
        succeeds. A failed upload is not a usable artifact. Do not execute or import
        uploaded code. Artifact references stay workspace-scoped and can be used across routes.
      parameters:
        - $ref: "#/components/parameters/IdempotencyKey"
      requestBody:
        required: true
        content:
          application/octet-stream:
            schema:
              type: string
              format: binary
            example: |
              build output
      responses:
        "201":
          description: Complete staged artifact and its retention deadline.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Artifact"
        "413":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /artifacts/{artifact_id}:
    parameters:
      - name: artifact_id
        in: path
        required: true
        description: Workspace-owned artifact ID.
        schema:
          type: string
          minLength: 1
    get:
      tags:
        - artifacts
      operationId: getArtifact
      x-speakeasy-group: artifacts
      x-speakeasy-name-override: get
      summary: Inspect a staged artifact
      description: Return metadata only. Unknown, unowned and expired artifacts return 404.
      responses:
        "200":
          description: Artifact size, digest and retention deadline.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Artifact"
        "404":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
    delete:
      tags:
        - artifacts
      operationId: deleteArtifact
      x-speakeasy-group: artifacts
      x-speakeasy-name-override: delete
      summary: Remove a staged artifact
      description: |
        Delete source storage without deleting resulting provider images. Reject with
        409 while a submitted build, file transfer or credential-factory activation
        still needs these bytes. An activated factory uses its retained encrypted
        copy; deleting staging bytes does not revoke that credential version. Successful deletion is
        idempotent for a known owned record; unowned IDs remain 404.
      responses:
        "204":
          description: Artifact bytes removed.
        "404":
          $ref: "#/components/responses/Error"
        "409":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /images/prepare:
    post:
      tags:
        - images
      operationId: prepareImage
      x-speakeasy-group: images
      x-speakeasy-name-override: prepare
      summary: Prepare an image on selected providers
      description: |
        Start background preparation using this workspace's payer-account rules, on the named
        `providers` or on the `top` providers a create with the `match` body would pick now.
        A named provider that cannot run the image reports its fit reason immediately; a
        `match` body that no route can run is refused as that create would be. With `match`,
        the response lists the picked providers in rank order, and each reason says how it
        was ranked. A ready result means a usable copy existed when preparation completed;
        it does not keep the copy awake afterward. Read progress from GET /images/preparations.
        OCI and Dockerfile inputs use the same declared preparation capabilities as a create.
        A Dockerfile response returns a content selector for status and unprepare. If no selected
        offering declares prepared Dockerfile/context support, it is refused before reading the
        context or starting provider work. Existing provider artifacts are already prepared.
        The request JSON is limited to 64 KiB; uploaded contexts retain the artifacts API limits.
      parameters:
        - $ref: "#/components/parameters/IdempotencyKey"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PrepareImage"
            example:
              match:
                image: python:3.13-slim
              top: 2
      responses:
        "202":
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          description: Preparation started or already in progress.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ImagePreparationStatus"
        "400":
          $ref: "#/components/responses/Error"
        "409":
          $ref: "#/components/responses/Error"
        "422":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /images/preparations:
    get:
      tags:
        - images
      operationId: getImagePreparations
      x-speakeasy-group: images
      x-speakeasy-name-override: status
      summary: Read image preparation status
      description: Reads preparation state recorded for the requested image in this workspace. The providers query can limit the result; omitted returns every recorded provider. A content selector reads the workspace's recorded copy without contacting a registry.
      parameters:
        - name: image
          description: |
            Image reference, `repo@sha256:digest`, or a Dockerfile preparation's returned
            `sha256:<64 lowercase hex>` selector. Content selectors read only this workspace's
            recorded copies without contacting a registry. A private tag works when this workspace has
            prepared or created it with registry_credentials before; otherwise name its digest.
          in: query
          required: true
          schema:
            type: string
            minLength: 1
        - name: providers
          in: query
          required: false
          description: Limit the status to these providers; omitted returns all recorded providers.
          schema:
            type: array
            minItems: 1
            uniqueItems: true
            items:
              type: string
              minLength: 1
          style: form
          explode: true
      responses:
        "200":
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          description: Current preparation state for the workspace's recorded provider copies.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ImagePreparationStatus"
        "400":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /images/unprepare:
    post:
      tags:
        - images
      operationId: unprepareImage
      x-speakeasy-group: images
      x-speakeasy-name-override: unprepare
      summary: Remove this workspace's prepared image copies
      description: |
        Remove only copies Cloud recorded for this workspace. Copies on managed accounts
        that other workspaces still use remain available, while this workspace's use is
        dropped. The image itself is not deleted. A provider reports gone only after it
        confirms removal. A later sandbox create may prepare the image again.
      parameters:
        - $ref: "#/components/parameters/IdempotencyKey"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UnprepareImage"
            example:
              image: python:3.13-slim
              providers: all
      responses:
        "200":
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          description: Removal outcome per selected provider.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ImagePreparationStatus"
        "400":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /sandboxes/{sandbox_id}/processes:
    parameters:
      - $ref: "#/components/parameters/SandboxId"
    post:
      tags:
        - processes
      operationId: startProcess
      x-speakeasy-group: processes
      x-speakeasy-name-override: start
      summary: Start a durable background command
      description: |
        Runs a command in the background in a Bash login shell and returns a process ID. The
        process keeps running between your calls: read its output, write to its input, or cancel
        it later, from any client. The sandbox's lifetime bounds how long it can run; cancel it to
        end it sooner.

        The sandbox image needs Linux `/proc`, Bash, and a few standard tools (`setsid`, `nohup`,
        `tail`, `base64`). If one is missing, the call fails with `unsupported_feature` before
        anything starts.

        `stdin` defaults to `false`. Setting it to `true`, to write to the process later, needs a
        sandbox created with the `processes.input` requirement and a route that supports writing to
        the process after it reconnects (`reattached_stdin`). To check ahead of time which routes
        qualify, send the same requirements to `POST /sandboxes/match`.

        With `wait_for_port`, the call returns once that port accepts TCP connections inside the
        sandbox. If the process exits first, or the port is still closed after 30 seconds, the
        process is ended and the call fails with `409 port_not_listening`. `details` carries
        `port`, `stdout_tail`, `stderr_tail` and, when the process exited, `exit_code`.
      parameters:
        - $ref: "#/components/parameters/IdempotencyKey"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/StartProcess"
            example:
              command: python3 -m http.server 3000
              wait_for_port: 3000
      responses:
        "201":
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          description: Durable process handle created.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Process"
        "404":
          $ref: "#/components/responses/Error"
        "409":
          $ref: "#/components/responses/Error"
        "422":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /sandboxes/{sandbox_id}/processes/{process_id}:
    parameters:
      - $ref: "#/components/parameters/SandboxId"
      - $ref: "#/components/parameters/ProcessId"
    get:
      tags:
        - processes
      operationId: readProcess
      x-speakeasy-group: processes
      x-speakeasy-name-override: read
      summary: Read process output from byte offsets
      description: |
        **Process observation**

        You can rely on:
        - A completed result carries the exact exit status and the complete output; otherwise the call reports lost observation or incomplete output.
        - Output bytes are exact on every route (commands in byte mode stay exact everywhere).

        You can set: observation deadline. Reattach where the route supports it.

        Not promised: chunk sizes; immediate status visibility; output kept beyond the retained history.

        **Process wait**

        You can rely on:
        - Returns the exact observed exit status.
        - An observation deadline does not claim the process stopped.

        You can set: observation deadline.

        Returns up to 256 KiB per stream from the requested byte offsets. Pass the
        returned next offsets to continue without gaps or repeats. `exit_code` is set
        only after the process ended and both streams were read to their ends. Reads
        are non-blocking. If the provider replaced the guest session, the answer is
        410 `expired`; the command is never run again.
      parameters:
        - name: stdout
          in: query
          schema:
            type: integer
            minimum: 0
            default: 0
        - name: stderr
          in: query
          schema:
            type: integer
            minimum: 0
            default: 0
      responses:
        "200":
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          description: Retained output starting at the given offsets.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProcessOutput"
        "404":
          $ref: "#/components/responses/Error"
        "410":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
    delete:
      tags:
        - processes
      operationId: removeProcess
      x-speakeasy-group: processes
      x-speakeasy-name-override: remove
      summary: Delete a finished process's retained output
      description: Deletes the retained output and metadata for a finished process in the addressed sandbox. It does not stop a running process; that request returns 409. The SDK generates an Idempotency-Key when omitted; reuse a supplied key with the identical request to reconcile an uncertain result.
      parameters:
        - $ref: "#/components/parameters/IdempotencyKey"
      responses:
        "204":
          description: Removed.
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
        "404":
          $ref: "#/components/responses/Error"
        "409":
          $ref: "#/components/responses/Error"
        "410":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /sandboxes/{sandbox_id}/processes/{process_id}/stdin:
    parameters:
      - $ref: "#/components/parameters/SandboxId"
      - $ref: "#/components/parameters/ProcessId"
    post:
      tags:
        - processes
      operationId: writeProcessStdin
      x-speakeasy-group: processes
      x-speakeasy-name-override: write_stdin
      summary: Write process input
      description: |
        You can rely on:
        - Input is delivered in order.
        - EOF is explicit.
        - Uncertain delivery is never resent.
        - Input is UTF-8 text on every route.

        You can set: data (text), EOF.

        Not promised: chunking on the wire.

        Append text to a process started with `stdin: true`. Calls from one client
        must be awaited sequentially to preserve input order. The selected route must
        support both `processes.input` and input after process reconnection
        (`reattached_stdin`); otherwise writes and EOF are refused as `unsupported_feature`
        before dispatch. Use `POST /sandboxes/match` with `requirements.processes.input`
        set to true to inspect eligible routes before create. If stdin is closed, return 409.
      parameters:
        - $ref: "#/components/parameters/IdempotencyKey"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/WriteStdin"
            example:
              data: |
                yes
      responses:
        "204":
          description: Input accepted by the provider; application consumption is not guaranteed.
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
        "404":
          $ref: "#/components/responses/Error"
        "409":
          $ref: "#/components/responses/Error"
        "410":
          $ref: "#/components/responses/Error"
        "422":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /sandboxes/{sandbox_id}/processes/{process_id}/stdin/close:
    parameters:
      - $ref: "#/components/parameters/SandboxId"
      - $ref: "#/components/parameters/ProcessId"
    post:
      tags:
        - processes
      operationId: closeProcessStdin
      x-speakeasy-group: processes
      x-speakeasy-name-override: close_stdin
      summary: Send stdin EOF
      description: |
        You can rely on:
        - Input is delivered in order.
        - EOF is explicit.
        - Uncertain delivery is never resent.
        - Input is UTF-8 text on every route.

        You can set: data (text), EOF.

        Not promised: chunking on the wire.

        Close only the input stream of a process started with stdin enabled; unsupported providers refuse before dispatch.
      responses:
        "204":
          description: Stdin closure confirmed.
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
        "404":
          $ref: "#/components/responses/Error"
        "410":
          $ref: "#/components/responses/Error"
        "422":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /sandboxes/{sandbox_id}/processes/{process_id}/cancel:
    parameters:
      - $ref: "#/components/parameters/SandboxId"
      - $ref: "#/components/parameters/ProcessId"
    post:
      tags:
        - processes
      operationId: cancelProcess
      x-speakeasy-group: processes
      x-speakeasy-name-override: cancel
      summary: Cancel a durable process
      description: |
        Ends the process and everything it started. This works on every sandbox; it needs no
        `processes.cancel` requirement. Cancelling a process that has already finished succeeds and
        changes nothing.
      parameters:
        - $ref: "#/components/parameters/IdempotencyKey"
      responses:
        "204":
          description: Cancellation acknowledged, or process already exited.
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
        "404":
          $ref: "#/components/responses/Error"
        "409":
          $ref: "#/components/responses/Error"
        "410":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /credits:
    get:
      tags:
        - credits
      operationId: getCredits
      x-speakeasy-group: credits
      x-speakeasy-name-override: balance
      summary: Get the workspace credit balance
      description: |
        Requires account-management permission; execution keys receive 403. Returns 503
        payments_unavailable while credit purchases are not configured. BYOK work never
        depends on this balance.
      responses:
        "200":
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          description: Authoritative ledger balance and accepted top-up bounds.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CreditBalance"
        "403":
          $ref: "#/components/responses/Error"
        "503":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /spend-limits:
    get:
      tags:
        - spend-limits
      operationId: listSpendLimits
      x-speakeasy-group: spend_limits
      x-speakeasy-name-override: list
      summary: List managed-compute spend limits
      description: |
        Requires account-management permission; execution keys receive 403. Returns the
        workspace cap and every key's cap; a scope with no cap has `spend_limit: null`.
        Each capped scope also carries `spent` so far in its current period, holds included:
        `workspace_spent_nanodollars` for the workspace and `spent_nanodollars` per key.
        Limits and usage are scoped to the configured test or live billing book. BYOK usage
        is excluded.
      responses:
        "200":
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          description: The workspace cap.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SpendLimitList"
        "403":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /spend-limits/{scope}:
    parameters:
      - name: scope
        in: path
        required: true
        description: |
          What the cap applies to. Only `workspace` exists: it covers every API key in the
          workspace. Any other value returns 400 `invalid_request` with `details.field` `scope`.
        schema:
          type: string
          enum:
            - workspace
    put:
      tags:
        - spend-limits
      operationId: setSpendLimit
      x-speakeasy-group: spend_limits
      x-speakeasy-name-override: set
      summary: Set a managed-compute spend cap
      description: |
        Requires account-management permission. The cap applies to the scope's settled
        managed charges and open managed holds. `limit_period` sets when it resets: `daily`
        and `monthly` at 00:00 UTC (monthly on the first of the month), `total` never.
        Replaces any cap the scope already has. BYOK usage is excluded.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SpendLimit"
            example:
              limit_usd: "25.00"
              limit_period: monthly
      responses:
        "200":
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          description: The scope's cap after replacement.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SpendLimitRecord"
        "400":
          $ref: "#/components/responses/Error"
        "403":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
    delete:
      tags:
        - spend-limits
      operationId: clearSpendLimit
      x-speakeasy-group: spend_limits
      x-speakeasy-name-override: clear
      summary: Clear a managed-compute spend cap
      description: |
        Requires account-management permission. Removes the scope's cap. Clearing a scope
        with no cap succeeds and returns `spend_limit: null`.
      responses:
        "200":
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          description: The scope with no cap.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SpendLimitRecord"
        "400":
          $ref: "#/components/responses/Error"
        "403":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /credits/purchases:
    post:
      tags:
        - credits
      operationId: createCreditPurchase
      x-speakeasy-group: credits
      x-speakeasy-name-override: purchase
      summary: Start a credit purchase through Checkout or a saved card
      description: |
        Without payment_method_id, records a pending purchase and returns the hosted
        Checkout page; success_url and cancel_url are required for this path. The response
        reports fee_minor and total_minor before redirect: Checkout charges total_minor
        (amount_minor plus BoxRouter's purchase fee, before tax) in two line items, and only
        amount_minor is added as credit. A refund returns credit only; the fee is not
        refunded. With a
        workspace-owned payment_method_id, creates and pays a
        Stripe invoice using that card. A verified paid invoice returns confirmed; when
        payer authentication is required, returns pending with the hosted invoice
        authentication_url. Send the payer there, then poll this purchase after they
        return to the dashboard. Credit is added only after BoxRouter fetches and validates
        the paid invoice. A definitive card decline returns payment_declined with the
        purchase ID and a short safe decline reason in error details, outcome not_started;
        the failed purchase remains in history. Repeating a request with the same
        Idempotency-Key returns its original result; a different amount or card under that
        key is a 409. After a payment_provider_unavailable response with outcome unknown,
        retry with the same key.
      parameters:
        - $ref: "#/components/parameters/RequiredIdempotencyKey"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateCreditPurchase"
            example:
              amount_minor: 2500
              payment_method_id: pm_1Example
              expected_total_minor: 2500
      responses:
        "201":
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          description: Checkout purchase, saved-card purchase awaiting authentication, or saved-card purchase confirmed paid.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CreatedCreditPurchase"
        "400":
          $ref: "#/components/responses/Error"
        "402":
          description: The saved-card charge was declined and its failed purchase is retained.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CreditPurchaseDeclined"
        "403":
          $ref: "#/components/responses/Error"
        "409":
          $ref: "#/components/responses/Error"
        "502":
          $ref: "#/components/responses/Error"
        "503":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
    get:
      tags:
        - credits
      operationId: listCreditPurchases
      x-speakeasy-group: credits
      x-speakeasy-name-override: purchases
      summary: List recent credit purchases
      description: Workspace-owned purchases in every state, ordered by creation time and ID descending. Invoice and receipt links appear only after confirmed payment. New purchases do not shift subsequent pages. Invalid or another workspace's cursor is a 400.
      parameters:
        - $ref: "#/components/parameters/Limit"
        - $ref: "#/components/parameters/Cursor"
      responses:
        "200":
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          description: Purchase page, newest first; next_cursor is null at the end.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CreditPurchaseList"
        "400":
          $ref: "#/components/responses/Error"
        "403":
          $ref: "#/components/responses/Error"
        "503":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /credits/purchases/quote:
    post:
      tags:
        - credits
      operationId: quoteCreditPurchase
      x-speakeasy-group: credits
      x-speakeasy-name-override: quote_purchase
      summary: Preview a credit purchase and its tax
      description: Returns Stripe's current tax preview for the requested top-up amount. Requires account-management permission and is available only when in-page card purchases are enabled.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreditPurchaseQuoteRequest"
            example:
              amount_minor: 2500
      responses:
        "200":
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          description: Current purchase and tax quote.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CreditPurchaseQuote"
        "400":
          $ref: "#/components/responses/Error"
        "403":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        "409":
          $ref: "#/components/responses/Error"
        "422":
          $ref: "#/components/responses/Error"
        "429":
          $ref: "#/components/responses/Error"
        "502":
          $ref: "#/components/responses/Error"
        "503":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /credits/usage:
    get:
      tags:
        - credits
      operationId: listCreditUsage
      x-speakeasy-group: credits
      x-speakeasy-name-override: usage
      summary: List managed-compute credit movements
      description: |
        Requires account-management permission; execution keys receive 403. Returns
        confirmed, nonzero charges from the configured test or live billing book, the later
        corrections to them, reservations still held, and reservations released with no
        charge, newest first. Results are limited to the calling workspace. BYOK work,
        supplier costs, provider account identities, API-key digests and raw usage evidence
        are not returned; a row's `key_id` names the workspace API key it belongs to. A
        cursor belongs to this workspace and billing mode; invalid or foreign cursors
        return 400. New rows do not shift subsequent pages. No provider or payment-provider
        calls are made.
      parameters:
        - name: limit
          in: query
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
        - name: cursor
          in: query
          required: false
          schema:
            type: string
            minLength: 1
            maxLength: 512
      responses:
        "200":
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          description: Settled charge page; the amount is the credit debited, not provider cost.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ManagedCreditUsageList"
        "400":
          $ref: "#/components/responses/Error"
        "403":
          $ref: "#/components/responses/Error"
        "503":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /usage:
    get:
      tags:
        - credits
      operationId: listUsageReport
      x-speakeasy-group: usage
      x-speakeasy-name-override: list
      summary: List grouped managed and BYOK sandbox usage
      description: |
        Requires account-management permission; execution-only keys receive 403. Reads
        only this workspace and the configured test or live billing book. Settled managed
        charges come from verified ledger receipts and are recognized on their settlement
        date. BYOK costs come from the provider's own cost or usage records priced at
        stored list rates. byok_provider_cost_usd is that exact amount or null, never a
        local-clock cost or partial subtotal. No BYOK fee is included because its price-book policy remains
        proposed and unapproved. Current managed reservations are reported as held amounts;
        with group_by=day they appear under period and group_key `current`, outside the
        requested historical date range. Dates are inclusive UTC calendar dates and the
        range may not exceed 92 days. Unknown observations remain null and are counted in
        the corresponding unknown-sandbox field; a BYOK cost is null when any contributing
        sandbox's provider cost is unknown. The optional provider filter is applied
        before grouping to managed and BYOK history and to current managed holds. Cursors
        are scoped to workspace, billing mode and all report filters; malformed or foreign
        cursors return 400. No provider or
        payment-provider calls are made.
      parameters:
        - name: from
          in: query
          required: true
          schema:
            type: string
            format: date
            pattern: ^[0-9]{4}-[0-9]{2}-[0-9]{2}$
        - name: to
          in: query
          required: true
          schema:
            type: string
            format: date
            pattern: ^[0-9]{4}-[0-9]{2}-[0-9]{2}$
        - name: group_by
          in: query
          required: false
          schema:
            type: string
            enum:
              - provider
              - day
              - key
              - sandbox
            default: provider
        - name: provider
          in: query
          required: false
          description: Restrict rows to this provider before grouping; values use canonical provider identifiers.
          schema:
            type: string
            minLength: 1
            maxLength: 64
            pattern: ^[a-z][a-z0-9-]{0,63}$
        - name: cursor
          in: query
          required: false
          schema:
            type: string
            minLength: 1
            maxLength: 512
        - name: limit
          in: query
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 1000
            default: 100
      responses:
        "200":
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          description: One page of grouped usage with an observation timestamp and optional cursor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UsageReportPage"
        "400":
          $ref: "#/components/responses/Error"
        "403":
          $ref: "#/components/responses/Error"
        "503":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /usage.csv:
    get:
      tags:
        - credits
      operationId: exportUsageReportCsv
      x-speakeasy-group: usage
      x-speakeasy-name-override: export_csv
      summary: Export grouped managed and BYOK sandbox usage as CSV
      description: Uses the same workspace, billing mode, date range, filters and page as the JSON usage report. CSV values preserve the JSON report's exact decimal strings; `X-Usage-As-Of` identifies the observation time and `X-Next-Cursor` is present when another page exists.
      parameters:
        - name: from
          in: query
          required: true
          schema:
            type: string
            format: date
            pattern: ^[0-9]{4}-[0-9]{2}-[0-9]{2}$
        - name: to
          in: query
          required: true
          schema:
            type: string
            format: date
            pattern: ^[0-9]{4}-[0-9]{2}-[0-9]{2}$
        - name: group_by
          in: query
          required: false
          schema:
            type: string
            enum:
              - provider
              - day
              - key
              - sandbox
            default: provider
        - name: provider
          in: query
          required: false
          description: Restrict rows to this provider before grouping; values use canonical provider identifiers.
          schema:
            type: string
            minLength: 1
            maxLength: 64
            pattern: ^[a-z][a-z0-9-]{0,63}$
        - name: cursor
          in: query
          required: false
          schema:
            type: string
            minLength: 1
            maxLength: 512
        - name: limit
          in: query
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 1000
            default: 100
      responses:
        "200":
          headers:
            X-Usage-As-Of:
              required: true
              description: UTC timestamp for the consistent database snapshot used for this page.
              schema:
                type: string
                format: date-time
            X-Next-Cursor:
              required: false
              description: Workspace-scoped cursor for the next page; omitted at the end.
              schema:
                type: string
                minLength: 1
                maxLength: 512
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          description: |
            CSV export for the selected page. Header names are `period`, `group_key`,
            `sandbox_count`, `compute_seconds_low`, `compute_seconds_high`,
            `compute_seconds_unknown_sandboxes`, `charge_usd`, `held_usd`,
            `byok_provider_cost_usd`, and `byok_provider_cost_unknown_sandboxes`. Null
            values are empty CSV fields, distinct from numeric zero. For example, a row
            with an unknown group, compute interval, and BYOK provider cost could be:

            ```csv
            period,group_key,sandbox_count,compute_seconds_low,compute_seconds_high,compute_seconds_unknown_sandboxes,charge_usd,held_usd,byok_provider_cost_usd,byok_provider_cost_unknown_sandboxes
            2026-10-01/2026-10-02,,2,,,2,0.000000000,0.000000000,,2
            ```
          content:
            text/csv:
              schema:
                type: string
        "400":
          $ref: "#/components/responses/Error"
        "403":
          $ref: "#/components/responses/Error"
        "503":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /credits/purchases/{purchase_id}:
    parameters:
      - name: purchase_id
        in: path
        required: true
        description: Purchase ID owned by the calling workspace.
        schema:
          type: string
          minLength: 1
    get:
      tags:
        - credits
      operationId: getCreditPurchase
      x-speakeasy-group: credits
      x-speakeasy-name-override: get_purchase
      summary: Get one credit purchase
      description: The authoritative state to poll after the payer returns from Checkout. Another workspace's purchase is a 404.
      responses:
        "200":
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          description: Purchase state.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CreditPurchase"
        "400":
          $ref: "#/components/responses/Error"
        "403":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        "503":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /credits/purchases/{purchase_id}/cancel:
    parameters:
      - name: purchase_id
        in: path
        required: true
        description: Purchase ID owned by the calling workspace.
        schema:
          type: string
          minLength: 1
    post:
      tags:
        - credits
      operationId: cancelCreditPurchase
      x-speakeasy-group: credits
      x-speakeasy-name-override: cancel_purchase
      summary: Close an unpaid Checkout session
      description: |
        Expires a known open Checkout session for this workspace-owned purchase. A
        completed payment is reconciled and remains confirmed. If Checkout creation
        had an unknown outcome and no session ID was recorded, the same stable Stripe
        idempotency key recovers that request before BoxRouter expires the session.
        Repeating this request is safe. The request needs no body: the return URLs are
        optional and unused when closing a purchase.
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CancelCreditPurchase"
            example:
              cancel_url: https://boxrouter.ai/credits
      responses:
        "200":
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          description: Current purchase state after attempting to close Checkout.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CreditPurchase"
        "400":
          $ref: "#/components/responses/Error"
        "403":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        "503":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /credits/promotions:
    post:
      tags:
        - credits
      operationId: redeemCreditPromotion
      x-speakeasy-group: credits
      x-speakeasy-name-override: redeem
      summary: Redeem a promotion code for free credit
      description: |
        Adds the code's credit to the workspace balance, once per workspace. Codes are
        case-insensitive and surrounding spaces are ignored. An unknown, expired, disabled
        or fully used code is 404 promotion_code_invalid; these cases are not told apart.
        A new redemption answers 201. A code this workspace already redeemed is refused
        with 409 promotion_already_redeemed and details.redemption containing the original redemption;
        its message names the redemption date. No credit is granted again. Promotional
        credit does not expire. Requires account-management permission; 503 payments_unavailable while
        credits are not configured.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/RedeemCreditPromotion"
            example:
              code: WELCOME-EXAMPLE
      responses:
        "201":
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          description: The new redemption; read the balance again to see the new total.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CreditPromotion"
        "400":
          $ref: "#/components/responses/Error"
        "403":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        "409":
          $ref: "#/components/responses/Error"
        "503":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
    get:
      tags:
        - credits
      operationId: listCreditPromotions
      x-speakeasy-group: credits
      x-speakeasy-name-override: promotions
      summary: List redeemed promotion codes
      description: Promotion codes this workspace redeemed, newest first.
      responses:
        "200":
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          description: Redemptions, newest first.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CreditPromotionList"
        "403":
          $ref: "#/components/responses/Error"
        "503":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /credits/billing-details:
    get:
      tags:
        - credits
      operationId: getCreditBillingDetails
      x-speakeasy-group: credits
      x-speakeasy-name-override: billing_details
      summary: Get workspace billing details
      description: Requires account-management permission. Reads the workspace Stripe customer. When multiple customer tax IDs exist, tax_id is the newest by creation time (ID breaks ties). Unset details are null.
      responses:
        "200":
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          description: Current workspace billing details.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/BillingDetails"
        "403":
          $ref: "#/components/responses/Error"
        "503":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
    put:
      tags:
        - credits
      operationId: updateCreditBillingDetails
      x-speakeasy-group: credits
      x-speakeasy-name-override: update_billing_details
      summary: Replace workspace billing details
      description: Stores legal name, invoice email, address and one tax ID on the workspace Stripe customer, creating it when absent. Details apply to future invoices. PUT replaces all customer tax IDs with the supplied single ID, adding the replacement before deleting old IDs so a rejected replacement preserves them. Stripe tax ID verification can be pending or unsuccessful. Repeat an uncertain update with the same Idempotency-Key; a changed body under that key returns 409. Stripe rejection returns 502; the update can have partially completed, so read current details before correcting it.
      parameters:
        - $ref: "#/components/parameters/RequiredIdempotencyKey"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UpdateBillingDetails"
            example:
              name: Example Labs, Inc.
              email: billing@example.com
              address:
                line1: 1 Example Street
                line2: null
                city: Springfield
                state: IL
                postal_code: "62701"
                country: US
      responses:
        "200":
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          description: Workspace billing details after the update.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/BillingDetails"
        "400":
          $ref: "#/components/responses/Error"
        "403":
          $ref: "#/components/responses/Error"
        "409":
          $ref: "#/components/responses/Error"
        "502":
          $ref: "#/components/responses/Error"
        "503":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /credits/alerts:
    get:
      tags:
        - credits
      operationId: getCreditAlerts
      x-speakeasy-group: credits
      x-speakeasy-name-override: alerts
      summary: Get workspace credit alert settings
      description: Requires account-management permission. Returns workspace-owned settings only.
      responses:
        "200":
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          description: Current workspace credit alert settings.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CreditAlerts"
        "403":
          $ref: "#/components/responses/Error"
        "503":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
    put:
      tags:
        - credits
      operationId: updateCreditAlerts
      x-speakeasy-group: credits
      x-speakeasy-name-override: update_alerts
      summary: Replace workspace credit alert settings
      description: Atomically replaces the optional trigger level and evaluates the current available balance. Repeating identical settings is idempotent and requires no Idempotency-Key. Sends the owner one alert per crossing below the trigger level and one when available credit is zero or negative; a repeated evaluation does not resend. Re-arms at or above the trigger level and above zero respectively. BYOK work is unaffected by credit balance.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreditAlertsUpdate"
            example:
              low_balance_threshold_minor: 1000
      responses:
        "200":
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          description: Workspace credit alert settings after the update.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CreditAlerts"
        "400":
          $ref: "#/components/responses/Error"
        "403":
          $ref: "#/components/responses/Error"
        "409":
          $ref: "#/components/responses/Error"
        "502":
          $ref: "#/components/responses/Error"
        "503":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /credits/payment-methods:
    get:
      tags:
        - credits
      operationId: listCreditPaymentMethods
      x-speakeasy-group: credits
      x-speakeasy-name-override: payment_methods
      summary: List saved workspace payment methods
      description: Returns masked card details only. Saved methods can be charged for a workspace member's purchase or immediately when enabling auto top-up with a trigger level above the current balance.
      responses:
        "200":
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          description: Saved cards in the Stripe customer profile.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CreditPaymentMethodList"
        "403":
          $ref: "#/components/responses/Error"
        "503":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /credits/payment-methods/setup:
    post:
      tags:
        - credits
      operationId: createCreditPaymentMethodSetup
      x-speakeasy-group: credits
      x-speakeasy-name-override: setup_payment_method
      summary: Start hosted setup for a saved payment method
      description: |
        Creates a Stripe-hosted setup page and returns its URL. Setup does not charge the
        payer. The completed method can be charged for a later user-initiated purchase or immediately when enabling auto top-up with a trigger level above the current balance.
        Repeating the request with the same Idempotency-Key returns the same page.
      parameters:
        - $ref: "#/components/parameters/RequiredIdempotencyKey"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreatePaymentMethodSetup"
            example:
              success_url: https://boxrouter.ai/credits
              cancel_url: https://boxrouter.ai/credits
      responses:
        "201":
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          description: Payment method setup session.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/StartedPaymentMethodSetup"
        "400":
          $ref: "#/components/responses/Error"
        "403":
          $ref: "#/components/responses/Error"
        "409":
          $ref: "#/components/responses/Error"
        "502":
          $ref: "#/components/responses/Error"
        "503":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /credits/payment-methods/setup-intent:
    post:
      tags:
        - credits
      operationId: createCreditPaymentMethodSetupIntent
      x-speakeasy-group: credits
      x-speakeasy-name-override: setup_payment_method_intent
      summary: Start in-page saved card setup
      description: Creates a Stripe SetupIntent for in-page card collection. Requires account-management permission and the in-page cards feature.
      responses:
        "201":
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          description: Stripe client configuration for card setup.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CreditSetupIntent"
        "403":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        "429":
          description: Five card setup attempts were recorded for the organization in the last hour (card_setup_rate_limit).
          headers:
            Retry-After:
              description: Seconds until the organization may start another card setup.
              schema:
                type: integer
                minimum: 1
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiErrorResponse"
        "502":
          $ref: "#/components/responses/Error"
        "503":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /credits/payment-methods/order:
    put:
      tags:
        - credits
      operationId: reorderCreditPaymentMethods
      x-speakeasy-group: credits
      x-speakeasy-name-override: reorder_payment_methods
      summary: Set saved card charge order
      description: Replaces the order of every saved card and Link method for automatic top-up. Requires account-management permission and the multi-card top-up feature.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ReorderCreditPaymentMethods"
            example:
              payment_method_ids:
                - pm_1Example
                - pm_2Example
      responses:
        "200":
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          description: Saved payment methods in charge order.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CreditPaymentMethodList"
        "400":
          $ref: "#/components/responses/Error"
        "403":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        "409":
          $ref: "#/components/responses/Error"
        "503":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /credits/payment-method-setups/{setup_id}:
    parameters:
      - name: setup_id
        in: path
        required: true
        description: Setup ID owned by the calling workspace.
        schema:
          type: string
          minLength: 1
    get:
      tags:
        - credits
      operationId: getCreditPaymentMethodSetup
      x-speakeasy-group: credits
      x-speakeasy-name-override: get_payment_method_setup
      summary: Get a saved payment method setup status
      description: Reads Stripe's current setup state after the payer returns. Another workspace's setup is a 404.
      responses:
        "200":
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          description: Setup state.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PaymentMethodSetup"
        "403":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        "503":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /credits/payment-methods/{payment_method_id}:
    parameters:
      - name: payment_method_id
        in: path
        required: true
        description: Stripe payment method ID owned by the calling workspace.
        schema:
          type: string
          pattern: ^pm_[A-Za-z0-9_]+$
    delete:
      tags:
        - credits
      operationId: deleteCreditPaymentMethod
      x-speakeasy-group: credits
      x-speakeasy-name-override: delete_payment_method
      summary: Remove a saved workspace payment method
      description: |
        Detaches a saved card. Another workspace's payment method is a 404. If the card
        was the default, the next saved card becomes default; removing the last card
        disables auto top-up with disabled_reason payment_method_required.
      responses:
        "204":
          description: Payment method removed.
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
        "403":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        "503":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /credits/payment-methods/{payment_method_id}/default:
    parameters:
      - name: payment_method_id
        in: path
        required: true
        description: Stripe payment method ID owned by the calling workspace.
        schema:
          type: string
          pattern: ^pm_[A-Za-z0-9_]+$
    post:
      tags:
        - credits
      operationId: setCreditDefaultPaymentMethod
      x-speakeasy-group: credits
      x-speakeasy-name-override: set_default_payment_method
      summary: Set the workspace's default saved card
      description: |
        Makes this workspace-owned saved card the only default card. The first saved
        card becomes default automatically. This local setting is used by one-click
        purchases and auto top-up; repeating the request is safe. Another workspace's
        card is a 404.
      responses:
        "200":
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          description: The saved card, now the workspace default.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CreditPaymentMethod"
        "403":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        "503":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /credits/auto-top-up:
    get:
      tags:
        - credits
      operationId: getCreditAutoTopUp
      x-speakeasy-group: credits
      x-speakeasy-name-override: auto_top_up
      summary: Get automatic credit top-up settings
      description: Requires account-management permission. Auto top-up is off until explicitly enabled.
      responses:
        "200":
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          description: Workspace auto top-up settings and most recent attempt.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CreditAutoTopUp"
        "403":
          $ref: "#/components/responses/Error"
        "503":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
    put:
      tags:
        - credits
      operationId: updateCreditAutoTopUp
      x-speakeasy-group: credits
      x-speakeasy-name-override: update_auto_top_up
      summary: Replace automatic credit top-up settings
      description: |
        Stores the enabled state, trigger level and purchase amount. The amount must be
        within current purchase_bounds_minor and the trigger level must be zero or greater.
        Enabling requires a default saved card. Enabling with a trigger level above current
        available credit evaluates and attempts one top-up immediately. An attempt is
        made once per crossing below the trigger level and is never retried automatically. A
        decline or required authentication disables auto top-up and records the reason.
        Repeating the same settings is idempotent and does not start a second attempt for
        the same balance crossing. After a 503 with outcome unknown, retry the identical
        settings or GET the settings; inspect last_attempt.purchase_id and poll that
        purchase to reconcile the original attempt.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UpdateCreditAutoTopUp"
            example:
              enabled: true
              threshold_minor: 500
              amount_minor: 2500
      responses:
        "200":
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          description: Workspace auto top-up settings after evaluation.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CreditAutoTopUp"
        "400":
          $ref: "#/components/responses/Error"
        "403":
          $ref: "#/components/responses/Error"
        "409":
          $ref: "#/components/responses/Error"
        "503":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /sandboxes/{sandbox_id}/terminals:
    parameters:
      - $ref: "#/components/parameters/SandboxId"
    post:
      tags:
        - terminals
      operationId: openTerminal
      x-speakeasy-group: terminals
      x-speakeasy-name-override: open
      summary: Open a terminal
      description: |
        Opens a real PTY running a Bash login shell on any active provider. A small
        Python 3 helper in the guest owns the PTY, so the sandbox image must provide
        `python3`. Opens once and returns a BoxRouter terminal handle; the shell ends at
        `timeout_seconds`, on `kill`, when it exits, or with the sandbox. Terminal
        connections and output expire on API worker loss; the durable open record still
        prevents another dispatch. No automatic recovery or replay.
      parameters:
        - $ref: "#/components/parameters/IdempotencyKey"
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/OpenSandboxTerminal"
            example:
              columns: 120
              rows: 30
      responses:
        "201":
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          description: Terminal handle created.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SandboxTerminal"
        "404":
          $ref: "#/components/responses/Error"
        "422":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /sandboxes/{sandbox_id}/terminals/{terminal_id}:
    parameters:
      - $ref: "#/components/parameters/SandboxId"
      - $ref: "#/components/parameters/TerminalId"
    get:
      tags:
        - terminals
      operationId: getTerminal
      x-speakeasy-group: terminals
      x-speakeasy-name-override: get
      summary: Get observed terminal state
      description: Reads an owned terminal record without consuming output.
      responses:
        "200":
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          description: Terminal state; an unknown exit code remains null.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SandboxTerminal"
        "404":
          $ref: "#/components/responses/Error"
        "410":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /sandboxes/{sandbox_id}/terminals/{terminal_id}/events:
    parameters:
      - $ref: "#/components/parameters/SandboxId"
      - $ref: "#/components/parameters/TerminalId"
    get:
      tags:
        - terminals
      operationId: streamTerminalEvents
      x-boxrouter-stream-terminal-types:
        - exit
        - error
      x-speakeasy-group: terminals
      x-speakeasy-name-override: events
      summary: Stream terminal output and its end
      description: |
        You can rely on:
        - Input order is kept and output applies backpressure on PTY routes.
        - Closing an observer does not by itself end the process.

        You can set: input, dimensions. Observation controls where supported.

        Not promised: unlimited concurrent observers.

        Server-sent events. Each data field contains one JSON TerminalEvent; the SSE id
        is its sequence. Output events carry exact bytes as base64. An exit event follows
        all output. An error event may arrive after HTTP 200 and ends the stream without
        implying the shell stopped. EOF without an exit or error event is an incomplete
        stream, not success.

        Output is retained in a 1 MiB sliding window until 60 seconds after the shell
        ends. Last-Event-ID resumes after that event while it is retained; a dropped
        sequence returns 410, or an error event for a reader that falls behind, and is
        never skipped silently. Without Last-Event-ID the stream starts at the oldest
        retained event.
      parameters:
        - name: Last-Event-ID
          in: header
          description: Last fully consumed event sequence from this terminal stream.
          schema:
            type: string
            pattern: ^[0-9]+$
      responses:
        "200":
          description: Typed SSE stream with exactly one exit or error event on normal completion.
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
            X-Event-Retention-Seconds:
              description: Minimum retention after the shell ends for this stream's events.
              schema:
                type: integer
                minimum: 1
          content:
            text/event-stream:
              schema:
                $ref: "#/components/schemas/SandboxTerminalEventSSE"
        "404":
          $ref: "#/components/responses/Error"
        "410":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /sandboxes/{sandbox_id}/terminals/{terminal_id}/input:
    parameters:
      - $ref: "#/components/parameters/SandboxId"
      - $ref: "#/components/parameters/TerminalId"
    post:
      tags:
        - terminals
      operationId: writeTerminalInput
      x-speakeasy-group: terminals
      x-speakeasy-name-override: write
      summary: Write terminal input
      description: |
        You can rely on:
        - Input order is kept and output applies backpressure on PTY routes.
        - Closing an observer does not by itself end the process.

        You can set: input, dimensions. Observation controls where supported.

        Not promised: unlimited concurrent observers.

        Writes exact bytes to the PTY once. Await calls sequentially to preserve order.
        Each call is one command round trip on the provider. An uncertain write must not
        be replayed with a new key. Returns 409 after the shell has ended.
      parameters:
        - $ref: "#/components/parameters/IdempotencyKey"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SandboxTerminalInput"
            example:
              data_base64: bHMgLWxhCg==
      responses:
        "204":
          description: Input delivered to the PTY; application consumption is not guaranteed.
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
        "404":
          $ref: "#/components/responses/Error"
        "409":
          $ref: "#/components/responses/Error"
        "410":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /sandboxes/{sandbox_id}/terminals/{terminal_id}/resize:
    parameters:
      - $ref: "#/components/parameters/SandboxId"
      - $ref: "#/components/parameters/TerminalId"
    post:
      tags:
        - terminals
      operationId: resizeTerminal
      x-speakeasy-group: terminals
      x-speakeasy-name-override: resize
      summary: Resize the terminal window
      description: |
        You can rely on:
        - Changes the terminal's window size, or is refused where the sandbox cannot resize an open terminal.

        You can set: columns, rows.

        Not promised: when the running program notices the new size.

        The foreground program receives SIGWINCH. Where the sandbox cannot resize an open
        terminal, the refusal is `409 capability_unavailable`; open the terminal at the size
        you need.
      parameters:
        - $ref: "#/components/parameters/IdempotencyKey"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SandboxTerminalSize"
            example:
              columns: 120
              rows: 30
      responses:
        "204":
          description: Window size applied.
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
        "404":
          $ref: "#/components/responses/Error"
        "409":
          $ref: "#/components/responses/Error"
        "410":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /sandboxes/{sandbox_id}/terminals/{terminal_id}/kill:
    parameters:
      - $ref: "#/components/parameters/SandboxId"
      - $ref: "#/components/parameters/TerminalId"
    post:
      tags:
        - terminals
      operationId: killTerminal
      x-speakeasy-group: terminals
      x-speakeasy-name-override: kill
      summary: Hang up the terminal without destroying its sandbox
      description: |
        You can rely on:
        - Ends the terminal's shell without destroying the sandbox, or is refused where the sandbox cannot end it from outside; ending a terminal that already ended succeeds until the terminal expires.

        Not promised: that the shell has already ended when the call returns (its exit event shows when it does); that the whole process group receives the hang-up.

        It sends a hang-up to the shell's process group where the provider allows that, and
        otherwise ends the shell process. The terminal's exit event carries the shell's exit
        code, or -1 when, after a kill, the provider ends the terminal without reporting one. Closing an
        events connection never calls this. Where the sandbox cannot end a terminal's shell from
        outside, the refusal is `409 capability_unavailable`; type `exit` in the terminal or
        destroy the sandbox. A terminal expires 60 seconds after its shell ends: until then a
        kill answers 204, afterwards `410 expired`.
      parameters:
        - $ref: "#/components/parameters/IdempotencyKey"
      responses:
        "204":
          description: Hang-up delivered, or the shell had already ended.
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
        "404":
          $ref: "#/components/responses/Error"
        "409":
          $ref: "#/components/responses/Error"
        "410":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /secrets:
    get:
      tags:
        - secrets
      operationId: listSecrets
      x-speakeasy-group: secrets
      x-speakeasy-name-override: list
      summary: List workspace secret names
      description: |
        Requires `credentials:manage` on API keys; dashboard browser sessions are also
        authorized. An execution key alone cannot administer workspace secrets. Lists
        metadata only; secret values are never returned.
      responses:
        "200":
          description: Workspace secret names and timestamps.
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SecretList"
        "401":
          $ref: "#/components/responses/Error"
        "403":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /secrets/{name}:
    parameters:
      - name: name
        in: path
        required: true
        description: Workspace secret name.
        schema:
          $ref: "#/components/schemas/SecretName"
    put:
      tags:
        - secrets
      operationId: putSecret
      x-speakeasy-group: secrets
      x-speakeasy-name-override: put
      summary: Store a workspace secret
      description: |
        Requires `credentials:manage` on API keys; dashboard browser sessions are also
        authorized. An execution key alone cannot administer workspace secrets. Create or
        replace a workspace-scoped secret. The value is accepted only in this request body;
        it is never returned by this or any secret listing operation.
      parameters:
        - $ref: "#/components/parameters/IdempotencyKey"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PutSecret"
            example:
              value: example-secret-value
      responses:
        "204":
          description: Secret stored without returning its value.
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
        "400":
          description: Secret name or value is invalid.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/InvalidRequestErrorResponse"
        "403":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
    delete:
      tags:
        - secrets
      operationId: deleteSecret
      x-speakeasy-group: secrets
      x-speakeasy-name-override: delete
      summary: Delete a workspace secret
      description: |
        Requires `credentials:manage` on API keys; dashboard browser sessions are also
        authorized. An execution key alone cannot administer workspace secrets. Remove the
        named workspace secret and return no secret data.
      parameters:
        - $ref: "#/components/parameters/IdempotencyKey"
      responses:
        "204":
          description: Secret removed.
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
        "403":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /sandboxes/{sandbox_id}/previews:
    parameters:
      - $ref: "#/components/parameters/SandboxId"
    get:
      tags:
        - previews
      operationId: listPreviews
      summary: List a sandbox's open previews
      description: |
        You can rely on:
        - Lists the previews this sandbox has open, as recorded when they were opened; a resume forgets them, so the list is empty until one is opened again.

        You can set: cursor, limit.

        Not promised: that a listed preview still works.

        Records only: this call observes no provider.
      x-speakeasy-group: previews
      x-speakeasy-name-override: list
      parameters:
        - $ref: "#/components/parameters/Cursor"
        - $ref: "#/components/parameters/Limit"
      responses:
        "200":
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          description: The recorded previews of this sandbox.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PreviewPage"
        "404":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
    post:
      tags:
        - previews
      operationId: openPreview
      summary: Get a URL for a sandbox port
      description: |
        You can rely on:
        - A private preview never falls back to a public link, and a requested credential form never falls back to the other; a mode the sandbox cannot enforce is refused. A route whose declaration cannot enforce the mode is refused before anything is opened; a weaker preview a route does open is ended before the refusal, or the refusal says it may still be reachable and names its port and id.
        - Opening a port that already has a preview with the same access returns that preview instead of opening a second one.

        You can set: port, access, credential.

        Not promised: that an application is listening on the port; an expiry time unless one is enforced.

        Returns an HTTPS URL for one port in the sandbox and, when the link is private, where its
        credential is carried. The sandbox must have been created with the `previews`
        requirement naming that port and the access you ask for.

        404 means the sandbox does not exist in this workspace. A sandbox that exists but
        was not created through this API is refused with `409 invalid_state`; one that has
        ended answers `410 sandbox_ended`.

        A preview is never returned with other access than the request asked for. When the
        provider answers with one (for example a public link for a private request), the call
        is refused with `502 preview_access_refused` and the unwanted preview is ended where
        the provider allows. `details.port` names the port. `details.revoked` is true when it
        was ended and no access remains. When it could not be ended the outcome is `unknown`,
        the message says the preview may still be reachable, and `details.preview_id` and
        `details.url` name a public one (a private preview's address holds a credential and is
        never repeated). The operation stays open for reconciliation: do not send the call
        again; end the sandbox to be certain no access remains.
      x-sandbox-provider-spec-next-errors:
        - not_named
        - not_running
        - gate_unavailable
      x-speakeasy-group: previews
      x-speakeasy-name-override: open
      parameters:
        - $ref: "#/components/parameters/IdempotencyKey"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/OpenPreview"
            example:
              port: 3000
              access: public
      responses:
        "200":
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          description: This port already had a preview with this access, so it is returned unchanged.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Preview"
        "201":
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          description: Preview URL and, for a private link, where its credential is.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Preview"
        "404":
          $ref: "#/components/responses/Error"
        "409":
          $ref: "#/components/responses/Error"
        "410":
          $ref: "#/components/responses/Error"
        "422":
          $ref: "#/components/responses/Error"
        "502":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
      x-boxrouter-capability:
        kind: gate
        gate: previews
  /sandboxes/{sandbox_id}/previews/{preview_id}:
    parameters:
      - $ref: "#/components/parameters/SandboxId"
      - $ref: "#/components/parameters/PreviewId"
    delete:
      tags:
        - previews
      operationId: revokePreview
      summary: Revoke a preview
      description: |
        You can rely on:
        - Access through the named preview ends while the sandbox and its other previews keep running; revoking a preview that already ended succeeds.

        You can set: preview id.

        Ends a preview, named by the ID that opening it returned. The sandbox must have been created
        with `previews.revocable` set to `true`; otherwise the call fails with `not_named`, and `fix`
        shows the requirement to send. A sandbox that is not running fails with `not_running`, even
        if the preview has already ended. Otherwise, ending a preview that has already ended answers
        `204`.
      x-speakeasy-group: previews
      x-speakeasy-name-override: revoke
      x-sandbox-provider-spec-next-errors:
        - not_named
        - not_running
        - gate_unavailable
      parameters:
        - $ref: "#/components/parameters/IdempotencyKey"
      responses:
        "204":
          description: Access ended, or already ended.
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
        "404":
          $ref: "#/components/responses/Error"
        "409":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
      x-boxrouter-capability:
        kind: gate
        gate: previews
  /sandboxes/{sandbox_id}/browser:
    parameters:
      - $ref: "#/components/parameters/SandboxId"
    post:
      tags:
        - sandboxes
      operationId: ensureBrowser
      x-speakeasy-group: sandboxes
      x-speakeasy-name-override: browser
      summary: Prove a headless Chromium in the sandbox
      description: |
        You can rely on:
        - Returns only the automation channels that loaded a page served inside the sandbox; when none does, the call is refused.

        Not promised: installing a browser in the sandbox.

        Loads a page from a server inside the guest in the image's own headless Chromium
        and reads the CDP endpoint: Playwright and CDP on glibc images, CDP on Alpine.
        Create the sandbox from an image with Python 3 and Playwright with Chromium (or a
        Chromium binary); BoxRouter's standard image has neither. An image without them, or a sandbox
        where no channel passes, such as one with too little memory, is refused with
        `422 unsupported_feature`. Afterwards run browser scripts through commands or processes.
      parameters:
        - $ref: "#/components/parameters/IdempotencyKey"
      responses:
        "200":
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          description: The browser and the automation channels it proved.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Browser"
        "404":
          $ref: "#/components/responses/Error"
        "409":
          $ref: "#/components/responses/Error"
        "422":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /sandboxes/{sandbox_id}/pause:
    parameters:
      - $ref: "#/components/parameters/SandboxId"
    post:
      tags:
        - sandboxes
      operationId: pauseSandbox
      x-speakeasy-group: sandboxes
      x-speakeasy-name-override: pause
      summary: Pause a sandbox and keep it
      description: |
        You can rely on:
        - On return the sandbox is paused, and the result says whether running processes are kept or only files.
        - The remaining lifetime budget is preserved.

        You can set: lifetime.

        Stops compute and keeps the sandbox where it is. The sandbox must have been created with
        the `pause_resume` requirement. The result says what was kept (running processes, or
        only files) and how much lifetime remains. Resume the sandbox before doing more work in it.

        404 means the sandbox does not exist in this workspace. A sandbox that exists but
        was not created through this API is refused with `409 invalid_state`; one that has
        ended answers `410 sandbox_ended`.
      parameters:
        - $ref: "#/components/parameters/IdempotencyKey"
      responses:
        "200":
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          description: The pause result.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Paused"
        "404":
          $ref: "#/components/responses/Error"
        "409":
          $ref: "#/components/responses/Error"
        "410":
          $ref: "#/components/responses/Error"
        "422":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
      x-boxrouter-capability:
        kind: gate
        gate: pause_resume
  /sandboxes/{sandbox_id}/resume:
    parameters:
      - $ref: "#/components/parameters/SandboxId"
    post:
      tags:
        - sandboxes
      operationId: resumeSandbox
      x-speakeasy-group: sandboxes
      x-speakeasy-name-override: resume
      summary: Resume the same paused sandbox
      description: |
        You can rely on:
        - Resumes the same sandbox on its original provider and account, never a replacement.
        - On return the sandbox is running, and the result says whether processes from before the pause were lost.
        - The sandbox runs on with the lifetime budget it had left; resume takes no new budget.
        - Resuming a sandbox that is already running succeeds and changes nothing, so a retry is safe.

        Not promised: that previews opened before the pause still work.

        Starts a paused or stopped sandbox again and returns its remaining lifetime. Resume takes
        no fields: send no body or `{}`, and any other field fails with `400 invalid_request`. To
        change how long the sandbox may run, extend its lifetime, which works while it is running,
        paused or stopped.

        Resuming a sandbox that is already running changes nothing, so you can safely send the call
        again. 404 means the sandbox does not exist in this workspace. A sandbox that cannot be
        resumed fails with 409: `invalid_state` while it is being created or destroyed, or when it
        was not created through this API; `lifetime_used_up` when its lifetime is spent (extend it,
        then resume); `volume_lease_lost` or `volume_busy` when its Volume cannot follow. A sandbox
        that has ended answers `410 sandbox_ended`.

        Those refusals happen before anything is resumed and report `outcome: not_started`. If the
        provider resumed the sandbox and a later step failed, the outcome is never `not_started`:
        `502 volume_restore_failed` (the sandbox restarted without its Volume mounted) reports
        `outcome: partial` with an `operation_id`, and the sandbox is running without its Volume.
        Do not send the resume again; destroy the sandbox and create a new one with the Volume
        attached. A lifetime failure after the resume reports `outcome: unknown`; read the operation
        before acting.
      parameters:
        - $ref: "#/components/parameters/IdempotencyKey"
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/Resume"
            example: {}
      responses:
        "200":
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          description: The resumed sandbox's state and lifetime.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Resumed"
        "204":
          description: The sandbox was already running; nothing changed.
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
        "400":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        "409":
          $ref: "#/components/responses/Error"
        "410":
          $ref: "#/components/responses/Error"
        "422":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
      x-boxrouter-capability:
        kind: recovery
  /sandboxes/{sandbox_id}/lifetime:
    parameters:
      - $ref: "#/components/parameters/SandboxId"
    post:
      tags:
        - sandboxes
      operationId: extendSandboxLifetime
      summary: Extend a sandbox lifetime
      description: |
        You can rely on:
        - The value is the new total running-time budget, not an increment, so sending it again is safe; a budget shorter than the current one or than the running time already used is refused. On success the sandbox may run for at least the requested total; extending a paused or stopped sandbox sets the budget it resumes with.

        You can set: lifetime_seconds.

        Not promised: that the sandbox ends exactly when the budget runs out.

        Refused with not_named unless the creation request asked for resources.extendable_lifetime. It is
        not a re-match: the only check is the route's max_lifetime_seconds for this account,
        against the current run, and over it the answer is lifetime_limit. Allowed while
        running, paused or stopped: a paused or stopped sandbox resumes with the new budget.
        A total shorter than the current budget is invalid_request (400); a total below the
        running time already used is lifetime_limit (409).

        404 means the sandbox does not exist in this workspace. A sandbox that exists but
        was not created through this API is refused with `409 invalid_state`; one that has
        ended answers `410 sandbox_ended`.
      x-speakeasy-group: sandboxes
      x-speakeasy-name-override: extend
      x-sandbox-provider-spec-next-errors:
        - not_named
        - lifetime_limit
      parameters:
        - $ref: "#/components/parameters/IdempotencyKey"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: false
              required:
                - lifetime_seconds
              properties:
                lifetime_seconds:
                  type: integer
                  minimum: 1
                  description: The new total running-time budget, not an increment.
            example:
              lifetime_seconds: 1800
      responses:
        "200":
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          description: The lifetime as it now stands.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Lifetime"
        "400":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        "409":
          $ref: "#/components/responses/Error"
        "410":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
      x-boxrouter-capability:
        kind: resource
        resource: extendable_lifetime
  /sandboxes/{sandbox_id}/network-policy:
    parameters:
      - $ref: "#/components/parameters/SandboxId"
    post:
      tags:
        - sandboxes
      operationId: updateNetworkPolicy
      summary: Change a running sandbox's outbound network policy
      description: |
        You can rely on:
        - Connections opened after the call returns follow the new policy, and the sandbox keeps its processes and files.
        - An uncertain outcome is never resent.

        You can set: policy, allow, deny.

        Not promised: that connections already open when the call returns are closed; name lookups and DNS traffic.

        Switches the sandbox's outbound network policy, or changes its entries. Each call starts
        from the policy the sandbox has now: the last one this call applied, or else the one named
        when the sandbox was created (`open` if it named none).

        The sandbox must have been created with the `network_lockdown` requirement naming the
        settings the change needs. Otherwise the call fails with `not_named`, and `fix` shows the
        requirement to send:

        - Switching to a different policy needs `switchable`, and the target policy listed in
          `switches_to` unless it is the policy the sandbox was created with. Switching back to the
          created policy with different entries also needs `changeable`.
        - Changing the entries of the current policy needs `changeable`, even when the entries are
          the same. After one switch, a further call that names the new policy needs `changeable`.
        - Deny entries on an allowlist need `deny_wins`, and an HTTP rule among the entries needs
          `http_rules`.

        `422 unsupported_feature` with outcome `not_started` means the route cannot switch policies.
      x-speakeasy-group: sandboxes
      x-speakeasy-name-override: update_network_policy
      x-sandbox-provider-spec-next-errors:
        - not_named
        - gate_unavailable
        - invalid_request
        - not_found
      parameters:
        - $ref: "#/components/parameters/IdempotencyKey"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UpdateNetworkPolicy"
            example:
              policy: allowlist
              allow:
                - pypi.org
                - files.pythonhosted.org
      responses:
        "200":
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          description: The policy now enforced.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/NetworkPolicyUpdated"
        "400":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        "409":
          $ref: "#/components/responses/Error"
        "422":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
      x-boxrouter-capability:
        kind: gate
        gate: network_lockdown
  /provider-accounts:
    post:
      tags:
        - provider-accounts
      operationId: connectProviderAccount
      x-speakeasy-group: provider_accounts
      x-speakeasy-name-override: connect
      parameters:
        - $ref: "#/components/parameters/IdempotencyKey"
      summary: Connect a provider account
      description: |
        Saves your own provider credentials so BoxRouter can run sandboxes on that account. Needs
        the `provider-accounts:manage` permission on the API key; a signed-in dashboard session also
        works. This permission is separate from the access that creates sandboxes.

        BoxRouter first makes one read-only request to the provider with the credentials. A clear
        rejection fails with `422 credentials_rejected`; an unreachable provider or an inconclusive
        answer does not block the save. The credentials are then stored encrypted and never
        returned. The account is enabled at once. Connecting starts no sandbox and prepares
        nothing in your provider account: the first sandbox that needs the standard image
        prepares it, or run the standard image setup to prepare it now.

        If the key matches one you already connected (same provider, settings and last four
        characters), the account is still connected and the response carries a
        `duplicate_credential` warning naming the other account. Connecting or testing an account
        whose provider is not enabled for you fails with `409 provider_not_enabled`.

        Provider connection settings, such as a custom URL or proxy, and credentials that run your
        code need extra authority on the key. Submitting a URL or proxy does not by itself give
        BoxRouter network access to that host. For credentials that run your code, send the ID of an
        artifact you uploaded; an unknown or expired artifact fails with `404`. The response
        returns account metadata only, never the code, configuration or tokens. Sending the same
        request again reuses the recorded outcome, and a request still waiting on the provider
        fails with `409` instead of running your code a second time.

        If the provider later answers with `401` or `403`, the account's status becomes
        `credentials_rejected`; the status carries a safe cause and the action that restores access.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ConnectProviderAccount"
            example:
              name: my-account
              credentials:
                provider: example-provider
                api_key: example-api-key-value
      responses:
        "201":
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          description: Connected-account record, without credentials.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProviderAccount"
        "400":
          $ref: "#/components/responses/Error"
        "403":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        "409":
          $ref: "#/components/responses/Error"
        "422":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
    get:
      tags:
        - provider-accounts
      operationId: listProviderAccounts
      x-speakeasy-group: provider_accounts
      x-speakeasy-name-override: list
      summary: List connected accounts
      description: |
        Requires `provider-accounts:manage` on API keys; dashboard browser sessions are also
        authorized. Returns your workspace's account metadata and the list of providers enabled for you;
        it never returns stored credentials.
      parameters:
        - $ref: "#/components/parameters/Cursor"
        - $ref: "#/components/parameters/Limit"
      responses:
        "200":
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          description: Connected accounts in this workspace.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProviderAccountPage"
        "403":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /provider-accounts/test:
    post:
      tags:
        - provider-accounts
      operationId: testProviderCredentials
      x-speakeasy-group: provider_accounts
      x-speakeasy-name-override: test_credentials
      summary: Test provider credentials without saving them
      description: |
        Checks credentials without saving them. Needs the `provider-accounts:manage` permission on
        the API key; a signed-in dashboard session also works. BoxRouter makes the same read-only
        request to the provider as when it tests a saved account, using the credentials in the body.
        Nothing is stored and no account is created or changed, so no idempotency key is needed.

        Credentials that run your code are not accepted here. Connection settings, such as a custom
        URL, are accepted and choose where the request goes; they need extra authority on the key,
        and submitting a URL or proxy does not by itself give BoxRouter network access to that host.
        The response returns the provider's own status and body, up to 4096 characters, without
        adding a validation message.

        Fails with `409 provider_not_enabled` when this provider is not enabled for you.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/TestProviderCredentials"
            example:
              credentials:
                provider: example-provider
                api_key: example-api-key-value
      responses:
        "200":
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          description: Raw result of the read-only provider request.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProviderAccountTest"
        "400":
          $ref: "#/components/responses/Error"
        "403":
          $ref: "#/components/responses/Error"
        "409":
          $ref: "#/components/responses/Error"
        "413":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /provider-accounts/{account_id}:
    parameters:
      - $ref: "#/components/parameters/AccountId"
    get:
      tags:
        - provider-accounts
      operationId: getProviderAccount
      x-speakeasy-group: provider_accounts
      x-speakeasy-name-override: get
      summary: Get connected-account metadata
      description: |
        Requires `provider-accounts:manage` on API keys; dashboard browser sessions are also
        authorized. The account must be visible within the caller's workspace. Never reveals
        key material.
      responses:
        "200":
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          description: Account metadata and current credential version.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProviderAccount"
        "404":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
    delete:
      tags:
        - provider-accounts
      operationId: disconnectProviderAccount
      x-speakeasy-group: provider_accounts
      x-speakeasy-name-override: disconnect
      summary: Remove a connected account
      description: |
        Requires `provider-accounts:manage` on API keys; dashboard browser sessions are also
        authorized. Reject with 409 while owned
        resources, pending mutations or unresolved cleanup still depend on this
        credential. Never orphan sandboxes silently or transfer them to managed
        billing. Removes the stored secret; does not revoke the upstream token.
        For callable credentials, close all unreferenced factory workers and remove
        their retained code/configuration and acquired secrets. Pending acquisitions
        and retained credential-version dependencies also prevent disconnection.

        Removal waits for work already running on the account (standard-image setup,
        Actions runner-image checks), which can take minutes. With `wait=false` the
        request returns 202 at once with the account and `disconnect.state:
        disconnecting`, and finishes in the background: read the account until it
        returns 404 (removed) or `disconnect.state: failed`, whose `error` is the
        refusal this request would otherwise have returned, with the account still
        connected. Repeating the request while a removal runs returns the same 202 and
        starts nothing new.
      parameters:
        - name: wait
          in: query
          required: false
          description: |
            true (default) answers when the removal is finished, with 204 or the refusal.
            false answers 202 as soon as the removal is accepted.
          schema:
            type: string
            enum:
              - "true"
              - "false"
            default: "true"
      responses:
        "202":
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          description: Removal accepted (`wait=false`); the account shows its progress in `disconnect`.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProviderAccount"
        "204":
          description: Account disconnected and stored credentials removed.
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
        "404":
          $ref: "#/components/responses/Error"
        "409":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /provider-accounts/{account_id}/credentials:
    parameters:
      - $ref: "#/components/parameters/AccountId"
    put:
      tags:
        - provider-accounts
      operationId: replaceProviderCredentials
      x-speakeasy-group: provider_accounts
      x-speakeasy-name-override: replace_credentials
      parameters:
        - $ref: "#/components/parameters/IdempotencyKey"
      summary: Rotate a connected account's credentials
      description: |
        Replaces a connected account's credentials, for example to rotate a key. Needs the
        `provider-accounts:manage` permission on the API key (a signed-in dashboard session also
        works) and the account's current `expected_version`.

        The new credentials must belong to the same provider. BoxRouter makes one read-only request
        to the provider and refuses a clear rejection with `422 credentials_rejected`; it does not
        check which account the key belongs to. Requests already running keep the old credentials,
        and requests that start afterward use the new ones. A provider that is not enabled for you
        fails with `409 provider_not_enabled`.

        Leave out the connection settings to keep the existing ones. Settings you send must match the
        existing route; changing the endpoint, proxy or header identity needs a new connected
        account, not a key rotation. Credentials that run your code need extra authority on the key
        and an artifact you uploaded. If the new credentials fail to activate, the old version stays
        in use. A token refresh that already happened with the provider is not undone.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ReplaceCredentials"
            example:
              expected_version: 1
              credentials:
                provider: example-provider
                api_key: example-api-key-value
      responses:
        "200":
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          description: Account metadata with the incremented credential version.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProviderAccount"
        "404":
          $ref: "#/components/responses/Error"
        "409":
          $ref: "#/components/responses/Error"
        "422":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /provider-accounts/{account_id}/test:
    parameters:
      - $ref: "#/components/parameters/AccountId"
    post:
      tags:
        - provider-accounts
      operationId: testProviderAccount
      x-speakeasy-group: provider_accounts
      x-speakeasy-name-override: test
      summary: Test a saved provider account
      description: |
        Requires `provider-accounts:manage` on API keys; dashboard browser sessions are also
        authorized. The credential check makes one read-only provider request using the saved
        credentials and returns the provider's native status and body, limited to 4096 characters.
        A successful test can update account health and register provider event webhooks. It
        starts no sandbox and prepares no image; run the standard image setup to prepare the
        standard image before the first sandbox needs it. The response reports the credential
        check.
        Fails with `409 provider_not_enabled` when this provider is not enabled for you.
      responses:
        "200":
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          description: Raw result of the read-only provider request.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProviderAccountTest"
        "404":
          $ref: "#/components/responses/Error"
        "409":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /provider-accounts/{account_id}/standard-image/setup:
    parameters:
      - $ref: "#/components/parameters/AccountId"
    post:
      tags:
        - provider-accounts
      operationId: setupProviderStandardImage
      x-speakeasy-group: provider_accounts
      x-speakeasy-name-override: setup_standard_image
      parameters:
        - $ref: "#/components/parameters/RequiredIdempotencyKey"
      summary: Run the standard image setup proof
      description: |
        Requires `provider-accounts:manage` on API keys; dashboard browser sessions are also
        authorized. This explicit action first tests the saved key, then boots the provider's
        standard image, runs a bounded command, and destroys the proof sandbox. The provider
        may charge for that short sandbox. Readiness is recorded only after boot, command and
        cleanup succeed; failure returns the unavailable state and its setup fix. Requests are
        idempotent and must include an idempotency key.
      responses:
        "200":
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          description: Current readiness for the exact default-region standard image.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProviderStandardImage"
        "404":
          $ref: "#/components/responses/Error"
        "409":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /operations/{operation_id}:
    get:
      tags:
        - operations
      operationId: getOperation
      x-speakeasy-group: operations
      x-speakeasy-name-override: get
      summary: Get a mutation's known outcome
      description: |
        Read the workspace-owned mutation record identified in an error response.
        This lookup never repeats the mutation. Unknown remains unknown until
        reconciliation establishes an outcome.

        If a creation response was lost, explicitly resubmitting POST /sandboxes
        in the original workspace with the original Idempotency-Key and identical
        effective request body recovers a retained creation record. Completed
        creation returns the original sandbox or an error retaining its operation
        ID if observing the sandbox's current state fails. An unresolved record
        returns 409 with its operation ID without dispatching creation again.
        Different input returns 409. This POST can create a sandbox if no record
        exists; it is not a read-only lookup. Unknown records remain until resolved;
        completed records remain for at least 24 hours. Without the original key and request,
        or beyond guaranteed retention, inspect owned resources or use support
        before creating more work. Never change the key to bypass uncertainty.
      parameters:
        - name: operation_id
          in: path
          required: true
          description: Durable operation identifier returned by BoxRouter.
          schema:
            type: string
            minLength: 1
            pattern: ^[^\u0000]*$
      responses:
        "200":
          description: Last known outcome; no arbitrary upstream response payloads.
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Operation"
        "404":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /volumes:
    get:
      tags:
        - volumes
      operationId: listVolumes
      x-speakeasy-group: volumes
      x-speakeasy-name-override: list
      summary: List volumes
      description: Lists volumes in the caller's workspace using cursor-based pagination. The response includes the current storage usage and live-volume policy only when those values are available.
      parameters:
        - $ref: "#/components/parameters/Cursor"
        - $ref: "#/components/parameters/Limit"
      responses:
        "200":
          description: Volumes
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/VolumePage"
        "400":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
    post:
      tags:
        - volumes
      operationId: createVolume
      x-speakeasy-group: volumes
      x-speakeasy-name-override: create
      summary: Create a volume
      description: The service selects its configured live backend when this workspace's live-volume policy allows creation. Backend identity is neither a request field nor a response label.
      parameters:
        - $ref: "#/components/parameters/IdempotencyKey"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateVolume"
            example:
              name: project-files
      responses:
        "201":
          description: Created volume
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Volume"
        "400":
          $ref: "#/components/responses/Error"
        "409":
          $ref: "#/components/responses/Error"
        "413":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /volumes/{volume_id}:
    parameters:
      - $ref: "#/components/parameters/VolumeId"
    get:
      tags:
        - volumes
      operationId: getVolume
      x-speakeasy-group: volumes
      x-speakeasy-name-override: get
      summary: Get a volume
      description: Returns metadata for a BoxRouter-managed volume in the caller's workspace.
      responses:
        "200":
          description: Volume
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Volume"
        "404":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
    delete:
      tags:
        - volumes
      operationId: deleteVolume
      x-speakeasy-group: volumes
      x-speakeasy-name-override: delete
      summary: Delete a volume and its checkpoints
      description: Deletes a volume and all checkpoint references and stored bytes for it. When the volume's writer is ending (its destroy was accepted), the call waits for the release, at most 120 seconds, before deleting. A running writer fails at once with volume_busy (409). Repeating DELETE retries a pending remote removal and returns 204 when the volume is already deleted. Reuse a supplied Idempotency-Key with the identical request to reconcile an uncertain result; a conflicting active use can return 409.
      parameters:
        - $ref: "#/components/parameters/IdempotencyKey"
      responses:
        "204":
          description: Volume and every checkpoint's bytes deleted.
        "404":
          $ref: "#/components/responses/Error"
        "409":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /volumes/{volume_id}/checkpoints:
    parameters:
      - $ref: "#/components/parameters/VolumeId"
    get:
      tags:
        - volumes
      operationId: listVolumeCheckpoints
      x-speakeasy-group: volumes
      x-speakeasy-name-override: list_checkpoints
      summary: List volume checkpoints
      description: Lists committed checkpoints for this volume, newest first. A checkpoint created by an in-progress save is not listed until the save commits.
      responses:
        "200":
          description: Checkpoints newest first
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/VolumeCheckpointPage"
        "404":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
    post:
      tags:
        - volumes
      operationId: saveVolume
      x-speakeasy-group: volumes
      x-speakeasy-name-override: save
      summary: Save a volume checkpoint
      description: Saves the current volume filesystem as an immutable checkpoint. The optional sandbox_id identifies the expected writer-lease holder; if omitted, the current holder is used. A mounted branch sandbox can be named to checkpoint its branch files without advancing the source volume's head. Read-only sandboxes cannot save. Reuse a supplied Idempotency-Key with the identical request to reconcile an uncertain response.
      parameters:
        - $ref: "#/components/parameters/IdempotencyKey"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SaveVolume"
            example:
              message: Before dependency upgrade
      responses:
        "201":
          description: Committed checkpoint
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/VolumeCheckpoint"
        "404":
          $ref: "#/components/responses/Error"
        "409":
          $ref: "#/components/responses/Error"
        "413":
          $ref: "#/components/responses/Error"
        "502":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /volumes/{volume_id}/checkpoints/{checkpoint}:
    parameters:
      - $ref: "#/components/parameters/VolumeId"
      - name: checkpoint
        in: path
        required: true
        schema:
          type: integer
          minimum: 1
    get:
      tags:
        - volumes
      operationId: getVolumeCheckpoint
      x-speakeasy-group: volumes
      x-speakeasy-name-override: get_checkpoint
      summary: Get a volume checkpoint
      description: Returns an immutable committed checkpoint by its volume ID and checkpoint number.
      responses:
        "200":
          description: Volume checkpoint
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/VolumeCheckpoint"
        "404":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
    delete:
      tags:
        - volumes
      operationId: deleteVolumeCheckpoint
      x-speakeasy-group: volumes
      x-speakeasy-name-override: delete_checkpoint
      summary: Delete a checkpoint
      description: Removes the checkpoint reference. Backend data is reclaimed when the volume is erased.
      parameters:
        - $ref: "#/components/parameters/IdempotencyKey"
      responses:
        "204":
          description: Checkpoint deleted.
        "404":
          $ref: "#/components/responses/Error"
        "409":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /volumes/{volume_id}/lease:
    parameters:
      - $ref: "#/components/parameters/VolumeId"
    delete:
      tags:
        - volumes
      operationId: releaseVolumeLease
      x-speakeasy-group: volumes
      x-speakeasy-name-override: release
      summary: Release a volume's writer lease
      description: Releases the volume's writer lease and advances its epoch, invalidating the former writer's lease token. Reuse a supplied Idempotency-Key with the identical request to reconcile an uncertain result.
      parameters:
        - $ref: "#/components/parameters/IdempotencyKey"
      responses:
        "204":
          description: Lease released and its epoch advanced.
        "404":
          $ref: "#/components/responses/Error"
        "409":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /volumes/{volume_id}/uploads:
    parameters:
      - $ref: "#/components/parameters/VolumeId"
    post:
      tags:
        - volumes
      operationId: createVolumeUpload
      x-speakeasy-group: volumes
      x-speakeasy-name-override: createUpload
      summary: Create a volume file upload
      description: |
        Creates a multipart upload for a file that will appear at
        `/workspace/<path>` in sandboxes. Upload every returned part URL with an
        HTTP PUT containing exactly that part's bytes; save the response `ETag`
        header for the completion request. Part URLs expire when the session does: six hours
        after creation plus about one hour for each 70 GiB of the declared size, at most 72 hours;
        `expires_at` says when.
        An open upload blocks `write` sandboxes, and a live or pending `write`
        attachment blocks uploads (`volume_busy`). `read` and `shared` attachments
        may coexist, but their sandboxes may need to refresh their volume cache to
        see the new file.
      parameters:
        - $ref: "#/components/parameters/IdempotencyKey"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateVolumeUpload"
            example:
              path: data/input.csv
              size: 1048576
      responses:
        "201":
          description: Created multipart upload
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/VolumeUpload"
        "400":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        "409":
          $ref: "#/components/responses/Error"
        "413":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /volumes/{volume_id}/uploads/{upload_id}/complete:
    parameters:
      - $ref: "#/components/parameters/VolumeId"
      - $ref: "#/components/parameters/UploadId"
    post:
      tags:
        - volumes
      operationId: completeVolumeUpload
      x-speakeasy-group: volumes
      x-speakeasy-name-override: completeUpload
      summary: Complete a volume file upload
      description: Supply each part number exactly once with the `ETag` header returned by its PUT response.
      parameters:
        - $ref: "#/components/parameters/IdempotencyKey"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CompleteVolumeUpload"
            example:
              parts:
                - number: 1
                  etag: '"9b2cf535f27731c974343645a3985328"'
      responses:
        "200":
          description: Completed volume file
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CompletedVolumeUpload"
        "400":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        "409":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /volumes/{volume_id}/uploads/{upload_id}:
    parameters:
      - $ref: "#/components/parameters/VolumeId"
      - $ref: "#/components/parameters/UploadId"
    delete:
      tags:
        - volumes
      operationId: abortVolumeUpload
      x-speakeasy-group: volumes
      x-speakeasy-name-override: abortUpload
      summary: Abort a volume file upload
      description: Aborts an open multipart upload for this volume. Repeating an abort is safe; the uploaded file is not made visible in the volume.
      parameters:
        - $ref: "#/components/parameters/IdempotencyKey"
      responses:
        "204":
          description: Upload aborted. Repeating an abort is safe.
        "404":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /volumes/{volume_id}/downloads:
    parameters:
      - $ref: "#/components/parameters/VolumeId"
    post:
      tags:
        - volumes
      operationId: createVolumeDownload
      x-speakeasy-group: volumes
      x-speakeasy-name-override: createDownload
      summary: Create a volume file download
      description: Creates a temporary download link that expires after one hour.
      parameters:
        - $ref: "#/components/parameters/IdempotencyKey"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateVolumeDownload"
            example:
              path: data/input.csv
      responses:
        "201":
          description: Created download link
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/VolumeDownload"
        "400":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /volumes/{volume_id}/files:
    parameters:
      - $ref: "#/components/parameters/VolumeId"
    get:
      tags:
        - volumes
      operationId: listVolumeFiles
      x-speakeasy-group: volumes
      x-speakeasy-name-override: listFiles
      summary: List files in a volume
      description: Recursively lists files in path order without a directory delimiter. Read and shared sandboxes may need to refresh their volume cache to see new files.
      parameters:
        - name: prefix
          in: query
          description: Return paths beginning with this relative volume path; may be empty or end in `/`.
          schema:
            $ref: "#/components/schemas/VolumeFilePrefix"
        - $ref: "#/components/parameters/Cursor"
        - name: limit
          in: query
          description: Maximum number of files to return.
          schema:
            type: integer
            minimum: 1
            maximum: 1000
            default: 100
      responses:
        "200":
          description: Volume files
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/VolumeFilePage"
        "400":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
    delete:
      tags:
        - volumes
      operationId: deleteVolumeFile
      x-speakeasy-group: volumes
      x-speakeasy-name-override: deleteFile
      summary: Delete a volume file
      description: Deletes the file at `path`, or every file under it when `path` ends in `/`, to free space without deleting the volume. Deleting a path that does not exist succeeds, so a repeated request is safe. A live or pending `write` attachment or an open upload makes the volume busy (`volume_busy`); read and shared sandboxes may need to refresh their volume cache to stop seeing the file.
      parameters:
        - name: path
          in: query
          required: true
          description: Relative volume path of one file, or a directory path ending in `/` to delete everything under it.
          schema:
            $ref: "#/components/schemas/VolumeFilePath"
      responses:
        "204":
          description: File or directory deleted, or it did not exist.
        "400":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        "409":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /sandboxes:
    post:
      tags:
        - sandboxes
      operationId: createSandbox
      x-sandbox-provider-spec-next-errors:
        - no_eligible_route
        - knob_not_named
        - unrecognised_word
      x-speakeasy-group: sandboxes
      x-speakeasy-name-override: create
      parameters:
        - $ref: "#/components/parameters/IdempotencyKey"
      summary: Create a sandbox
      description: |
        **create (standard image)**

        You can rely on:
        - Sandboxes are created from the requested image.

        You can set: image, lifetime, requirements.

        **volume mount**

        You can rely on:
        - The named checkpoint, or the head when none is named, is restored into the mount folder, replacing what the image has there; an empty volume restores nothing.

        You can set: volume.

        Not promised: mounting a provider's own volumes.

        Creates a sandbox and returns it once it is ready to run commands. Send `{}`, or no
        body, for the default: the standard image on the best eligible route, running for up
        to 300 seconds. A credit-funded sandbox on a route that can extend a lifetime runs
        instead for as long as your credit balance covers it when you leave out
        `lifetime_seconds`; `funded_until` on the sandbox says how far it is funded.

        Describe what you need and BoxRouter picks a route that meets all of it:

        - `requirements`: capabilities such as `pause_resume`, `previews` or
          `network_lockdown`, each with the settings you need.
        - `resources`: minimum CPU and memory, and a region.
        - `lifetime_seconds` and `at_end`: how long the sandbox may run, and whether it is
          destroyed or paused afterward.
        - `preferences`: how to choose among eligible routes (`sort`, `only`, `ignore`,
          `order`, `allow_fallbacks`).
        - `volume`: a Volume to mount, optionally at a checkpoint.
        - `secret_refs`: saved workspace secrets to add to the environment.

        BoxRouter never relaxes a requirement to find a cheaper or faster route. If no route
        can meet the request, the call fails with `409 no_eligible_route`, `details` names each
        route that was ruled out and the nearest changes, and nothing is created. A field that
        BoxRouter does not recognize fails with `400 invalid_request`; it is never ignored.
        Choosing a connected account or route whose provider is not enabled for you fails with
        `409 provider_not_enabled` before any route is considered. The response records the
        contract the sandbox was created under and why its route was chosen.

        A created sandbox does not mean your application is listening on a port yet. When one
        attempt's outcome is unclear, such as a timeout before the provider names a sandbox,
        BoxRouter never sends that attempt again: it moves to the next eligible route, unless
        `allow_fallbacks` is false, and destroys any sandbox the unclear attempt makes later. If
        the outcome of the whole create is unclear, read the operation named by `operation_id`
        to find out what happened.

        A `volume` has one writer at a time. If its previous writer is ending (its destroy
        was accepted, or cleanup of its access is pending), this call waits for the
        release, at most 120 seconds, and then creates the sandbox, so you need no polling or
        retry. If the release does not finish in that time, or the writer is still running,
        the call fails with `409 volume_busy`, outcome `not_started`, and
        `details.writer_sandbox_id` names the writer. A running writer fails at once.
      requestBody:
        required: false
        description: Omission is equivalent to an empty object, which is a baseline sandbox.
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateSandbox"
            examples:
              baseline:
                value: {}
              gates:
                value:
                  lifetime_seconds: 1800
                  at_end: pause
                  requirements:
                    pause_resume: {}
                    previews:
                      access: public
                      ports:
                        - 3000
                  resources:
                    memory_min_mib: 2048
                    region: EU
                  preferences:
                    sort: price
              connectedProvider:
                description: Uses a fictional connected account ID; replace it with an account you connected.
                value:
                  preferences:
                    only:
                      - example-provider
                  route:
                    account_id: acct_example
                    supply: byok
                  env:
                    TASK: example
      responses:
        "201":
          description: Sandbox created and its common runtime is ready.
          headers:
            X-Request-ID:
              $ref: "#/components/headers/RequestId"
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Sandbox"
        "400":
          $ref: "#/components/responses/Error"
        "401":
          $ref: "#/components/responses/Error"
        "402":
          $ref: "#/components/responses/Error"
        "409":
          $ref: "#/components/responses/Error"
        "422":
          $ref: "#/components/responses/Error"
        "429":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
    get:
      tags:
        - sandboxes
      operationId: listSandboxes
      x-speakeasy-group: sandboxes
      x-speakeasy-name-override: list
      summary: List workspace sandboxes
      description: |
        You can rely on:
        - Lists only the workspace's own sandboxes, each with its latest recorded state.
        - A label filter matches exactly: only sandboxes carrying every requested label are listed.

        You can set: filters, sort, cursor, limit.

        Not promised: a fresh observation of each sandbox from its provider.

        Lists your workspace's sandboxes from BoxRouter's records, without asking providers. `state`
        is the latest state BoxRouter recorded, or `unknown` if it recorded none; get one sandbox to
        check its provider live. `observed_at` is when the list was read, and `state_recorded_at` is
        when that state was recorded, which can be earlier.

        `lifecycle` defaults to `all`; send `lifecycle=live` to list only live sandboxes, or `ended` for
        the rest. You can repeat the `state` and `provider` parameters to match any
        of several values; repeated `label` parameters must all match. Unknown query parameters fail the request. The page counts apply every
        filter except `lifecycle`, and `by_state` also ignores `state`, so you can show counts while
        changing those two filters. `source` filters by what created the sandbox, and a sandbox's
        origin stays readable after it ends or is removed.
      parameters:
        - $ref: "#/components/parameters/Cursor"
        - $ref: "#/components/parameters/Limit"
        - name: lifecycle
          in: query
          description: Include live records, ended records, or both. Defaults to all.
          schema:
            type: string
            enum:
              - live
              - ended
              - all
            default: all
        - name: state
          in: query
          description: Portable lifecycle state; repeat to select multiple states (OR).
          style: form
          explode: true
          schema:
            type: array
            items:
              type: string
              enum:
                - creating
                - running
                - paused
                - destroying
                - destroyed
                - error
                - unknown
        - name: provider
          in: query
          description: Provider ID; repeat to select multiple providers (OR).
          style: form
          explode: true
          schema:
            type: array
            items:
              type: string
              minLength: 1
        - name: billed_by
          in: query
          description: Filter by upstream billing mode. byok alone includes all connected accounts.
          schema:
            type: string
            enum:
              - byok
              - managed
        - name: account_id
          in: query
          description: Connected account ID; by itself implies billed_by=byok. Invalid with billed_by=managed.
          schema:
            type: string
            minLength: 1
        - name: source
          in: query
          description: "Filter by what created the sandbox: github_actions (a BoxRouter Actions job), agents (an agent launch) or api (neither)."
          schema:
            type: string
            enum:
              - github_actions
              - agents
              - api
        - name: actions_run_id
          in: query
          description: Only sandboxes from this BoxRouter Actions run, as in origin.run_id.
          schema:
            type: string
            minLength: 1
            maxLength: 128
            pattern: ^[^\u0000]*$
        - name: launch_id
          in: query
          description: Only the sandbox from this agent launch, as in origin.launch_id.
          schema:
            type: string
            minLength: 1
            maxLength: 128
            pattern: ^[^\u0000]*$
        - name: label
          in: query
          description: "A label the sandbox must carry: key=value matches that exact value (the value may be empty), and a bare key matches any value. Repeat for labels that must all match."
          style: form
          explode: true
          schema:
            type: array
            items:
              type: string
              pattern: ^[^=]+(=.*)?$
        - name: created_after
          in: query
          description: Include sandboxes created at or after this RFC 3339 instant.
          schema:
            type: string
            format: date-time
        - name: created_before
          in: query
          description: Include sandboxes created before this RFC 3339 instant.
          schema:
            type: string
            format: date-time
        - name: q
          in: query
          description: Match BoxRouter ID prefix, labels.name substring (case-insensitive), or exact provider native ID.
          schema:
            type: string
            minLength: 1
            maxLength: 256
        - name: sort
          in: query
          description: Sort by creation time or indexed lifetime deadline; newest is the default. For ending_soonest, records with known deadlines sort first by ascending deadline, with BoxRouter ID ascending as the tie-breaker. Records with a null deadline follow in ascending BoxRouter ID order. source groups records by what created them, github_actions first, then agents, then api, newest first within each group.
          schema:
            type: string
            enum:
              - newest
              - oldest
              - ending_soonest
              - source
            default: newest
      responses:
        "200":
          description: A page of sandboxes belonging to this workspace. A sandbox with no recorded contract, such as one a provider-native route created, carries null `contract`.
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SandboxPage"
              examples:
                page:
                  summary: A workspace-scoped sandbox list page
                  description: The account ID in this example is fictional.
                  value:
                    data:
                      - id: sbx_4f2a
                        state: running
                        state_reason: requested
                        failure: null
                        native_state: null
                        observed_at: 2026-09-29T18:30:00Z
                        state_recorded_at: 2026-09-29T18:29:57Z
                        created_at: 2026-09-29T17:30:00Z
                        expires_at: 2026-09-29T19:30:00Z
                        ready_at: 2026-09-29T17:30:04Z
                        ended_at: null
                        hidden_secrets: null
                        tcp_ports: null
                        labels:
                          name: checkout-agent
                          team: platform
                        name: checkout-agent
                        resources:
                          cpu: 2
                          memory_mib: 4096
                          disk_mib: null
                        image:
                          value: standard
                          source: recorded
                        usage: null
                        cost:
                          label: calculated
                          total:
                            low: "0.0585"
                            high: "0.0585"
                        lifetime:
                          seconds: 7200
                          remaining_seconds: 3600
                          ends_at: 2026-09-29T19:30:04Z
                          kept_until: null
                          at_end: destroy
                          max_seconds: 86400
                          extendable: false
                        route:
                          id: route_01J8ZK2M6D8N4Q7S9T1V3W5X6Y
                          provider: example-provider
                          class: null
                          adapter_version: 0.3.9
                          account_id: connection:acct_example
                          supply: byok
                          native_id: iukabnkodrn7v36debdoe
                          region: us-east-1
                          selection: null
                        contract:
                          spec_version: sandbox-provider-spec/0.1
                          gates: {}
                          resources:
                            cpu: 2
                            memory_mib: 4096
                            disk_mib: null
                            region: us-east-1
                          attested: {}
                        request: {}
                        recorder: null
                        limits:
                          buffered_output_bytes: 16777216
                          file_transfer_bytes: 1048576
                    next_cursor: null
                    counts:
                      live: 12
                      ended: 3
                      by_state:
                        running: 8
                        paused: 2
        "401":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /sandboxes/match:
    post:
      tags:
        - sandboxes
      operationId: matchSandbox
      x-speakeasy-group: sandboxes
      x-speakeasy-name-override: match
      x-sandbox-provider-spec-next-errors:
        - no_eligible_route
        - knob_not_named
        - unrecognised_word
      summary: Explain routing without creating
      description: |
        You can rely on:
        - Evaluates the body exactly as create would and returns the routes create would try, in order, without provisioning, reserving or recording anything.

        You can set: creation body.

        Not promised: that a later create finds the same routes eligible.

        Each eligible route carries the contract it would grant, every excluded route the
        checks it failed, and the nearest changes are listed; a later creation checks again.
        A workspace with nothing to match against, that is no provider keys and no credits,
        or a `preferences.only` pin on a provider the workspace has not connected, answers 409 no_eligible_route with the same fix
        POST /sandboxes gives, because a dry run answers what the creation it stands for
        would.
      requestBody:
        required: false
        description: The creation body without request-only secret_values; omission is an empty object.
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/MatchSandbox"
            example:
              resources:
                memory_min_mib: 2048
              preferences:
                sort: price
      responses:
        "200":
          description: Routing explanation for this body. An empty eligible list is not an error when candidates were considered and excluded; a workspace with no candidate at all answers 409 no_eligible_route.
          headers:
            X-Request-ID:
              $ref: "#/components/headers/RequestId"
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MatchResult"
        "400":
          $ref: "#/components/responses/Error"
        "401":
          $ref: "#/components/responses/Error"
        "409":
          $ref: "#/components/responses/Error"
        "422":
          $ref: "#/components/responses/Error"
        "429":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /sandboxes/{sandbox_id}:
    parameters:
      - $ref: "#/components/parameters/SandboxId"
    get:
      tags:
        - sandboxes
      operationId: getSandbox
      x-boxrouter-cli-alias: get
      x-speakeasy-group: sandboxes
      x-speakeasy-name-override: get
      summary: Get a sandbox
      description: |
        You can rely on:
        - Returns a handle for the id, or not_found.

        You can set: sandbox id.

        Returns the sandbox with its current state, checked with the provider, without resuming it.
        `observed_at` is when the provider was checked. `state_recorded_at` is when BoxRouter last
        recorded the state it returns; it is null if a concurrent change replaced the state during
        the read. `origin` says what created the sandbox and stays readable after the sandbox ends or
        is removed, even when the provider can no longer be checked.
      responses:
        "200":
          description: The sandbox, with its account-bound route. A sandbox with no recorded contract, such as one a provider-native route created, carries null `contract`; its route still names the provider and account.
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Sandbox"
        "404":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
    delete:
      tags:
        - sandboxes
      operationId: destroySandbox
      x-boxrouter-cli-alias: destroy
      x-speakeasy-group: sandboxes
      x-speakeasy-name-override: destroy
      parameters:
        - name: discard_unsaved
          in: query
          required: false
          description: Destroy even if saving the attached volume fails, discarding unsaved changes.
          schema:
            type: boolean
            default: false
      summary: Destroy a sandbox
      description: |
        You can rely on:
        - Never deletes another generation of the sandbox.
        - Returns once accepted: 204 when confirmed, else 202 with an operation that completes lifecycle work; GET /operations/{id} reports completion.
        - While a destroy is unresolved, a repeated destroy is accepted as the same pending operation and makes no second provider call; once the destroy is confirmed, a repeat returns 204 with no body.

        You can set: nothing.

        Not promised: how long teardown takes; that the sandbox is already absent from listings on return.

        Requests destruction on the original upstream account, including persistent sandbox
        storage where the provider's destroy operation removes it. Separately owned volumes
        are not deleted. DELETE returns once the provider accepts the destroy, always as 202
        with the sandbox: `state` is `destroyed` when the provider confirmed within the request
        and `destroying` while lifecycle work remains, and `operation_id` is set while work
        remains. BoxRouter finishes an accepted destroy without the caller. GET
        /operations/{operation_id} reports the operation's outcome; for an accepted destroy this
        includes any remaining stop, final usage, and deletion work.

        A repeat DELETE while destruction is unresolved returns 202 with the existing
        operation_id and makes no second provider call. Once destruction is confirmed,
        repeat DELETEs return 202 with the destroyed sandbox and no `operation_id`. Unknown or
        unowned sandbox IDs return 404.
      responses:
        "202":
          description: The sandbox after the provider accepted the destroy.
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Sandbox"
        "404":
          $ref: "#/components/responses/Error"
        "409":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /sandboxes/{sandbox_id}/commands:
    parameters:
      - $ref: "#/components/parameters/SandboxId"
    post:
      tags:
        - commands
      operationId: runCommand
      x-speakeasy-group: commands
      x-speakeasy-name-override: run
      x-boxrouter-result-check:
        error: CommandFailed
        message: "Command failed: exit_code={exit_code}, timed_out={timed_out}"
        fail_when_any:
          - field: exit_code
            not_equal: 0
          - field: timed_out
            equal: true
        error_fields:
          - exit_code
          - stdout
          - stderr
          - timed_out
      parameters:
        - $ref: "#/components/parameters/IdempotencyKey"
      summary: Run a buffered command
      description: |
        **Commands**

        You can rely on:
        - Runs in a login Bash, so PATH changes that installers write to ~/.profile apply to later commands.
        - Returns the exit status.
        - Returns stdout and stderr separately.
        - Returns exact bytes in byte mode.
        - The timeout ends the command.
        - On a timeout the command's process group receives TERM at the deadline and KILL 5 seconds later, in both output modes.
        - Returned output is captured after the shell exits, and later writes by background children cannot change it.
        - An uncertain outcome is never replayed.
        - Output over the limit returns the exit status and the first output_limit_bytes of each stream, marked truncated. Default and maximum: 512 KiB per stream. Processes stream complete large output.

        You can set: command, cwd, env, timeout_seconds, output (text or bytes), output_limit_bytes.

        Not promised: guest disk used by output above the limit; ending children that leave the command's process group (for example setsid daemons).

        **Command stdin**

        You can rely on:
        - A command takes no input: it starts with stdin at end-of-file, and login profiles, the command and its children all read empty input.

        You can set: nothing (use process input for interactive input).

        Output over output_limit_bytes is not an error: the result sets stdout_truncated or
        stderr_truncated. A nonzero exit is a completed workload result, not an HTTP failure.
        A command that reaches timeout_seconds is a result too: the process group is ended and
        the answer is 200 with timed_out true, exit_code null and the output written so far.
        504 timeout is left for the case where BoxRouter lost sight of the command and cannot
        say whether it ran. A disconnected HTTP client is not promised to end the command; the
        timeout ends it. Use process streaming for output without a bound.
        Generated clients take an opt-in check argument and raise CommandFailed (the operation's
        x-boxrouter-result-check) when exit_code is not 0 or timed_out is true.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/RunCommand"
            example:
              command: printf hello
              timeout_seconds: 60
      responses:
        "200":
          description: Command exited or reached its timeout; stdout and stderr are separate and hold everything it wrote, up to the limit.
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: "#/components/schemas/CommandResult"
                  - $ref: "#/components/schemas/ByteCommandResult"
        "400":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        "409":
          $ref: "#/components/responses/Error"
        "422":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /sandboxes/{sandbox_id}/files/content:
    parameters:
      - $ref: "#/components/parameters/SandboxId"
      - name: path
        in: query
        required: true
        description: Absolute path inside the sandbox. Encoded as a query value, never a host path.
        schema:
          type: string
          minLength: 1
          pattern: ^/[^\u0000]*$
    get:
      tags:
        - files
      operationId: readFile
      x-boxrouter-cli-alias: read
      x-speakeasy-group: files
      x-speakeasy-name-override: read
      summary: Read exact file bytes
      description: |
        You can rely on:
        - Returns complete bytes, or resource_limit with no prefix returned.
        - Reading a directory or another path that is not a regular file fails and returns no data.
        - An absent path is not_found, outcome failed: a read has no effect, so its outcome is never unknown.

        You can set: path.

        Not promised: consistency while another writer changes the file.

        The limit is the advertised limits.file_transfer_bytes. Bytes are not text-decoded.
        Closing the transfer does not destroy the sandbox. A stream that ends before
        Content-Length is an incomplete transfer, not a successful file read.
      responses:
        "200":
          description: File bytes. Empty files have an empty response body.
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
            Content-Length:
              description: Total expected bytes when known before streaming.
              schema:
                type: integer
                minimum: 0
          content:
            application/octet-stream:
              schema:
                type: string
                format: binary
        "404":
          $ref: "#/components/responses/Error"
        "409":
          $ref: "#/components/responses/Error"
        "413":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
    put:
      tags:
        - files
      operationId: writeFile
      x-boxrouter-cli-alias: write
      x-speakeasy-group: files
      x-speakeasy-name-override: write
      parameters:
        - $ref: "#/components/parameters/IdempotencyKey"
      summary: Write exact file bytes
      description: |
        You can rely on:
        - Writes exact bytes.
        - Creates missing parents.
        - A new file is readable and writable by the command user.
        - The size limit is enforced.

        You can set: path, data.

        Not promised: owner, mode or executable bit; atomic replace; whether a symbolic link at the path is followed or replaced.

        The limit is the advertised limits.file_transfer_bytes. A failed transfer may leave
        partial data; the error's outcome describes what is known. SDK text helpers must encode
        text as UTF-8 before transfer.
      requestBody:
        required: true
        content:
          application/octet-stream:
            schema:
              type: string
              format: binary
            example: |
              hello world
      responses:
        "204":
          description: All bytes written successfully.
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
        "400":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        "413":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /sandboxes/{sandbox_id}/files/uploads:
    parameters:
      - $ref: "#/components/parameters/SandboxId"
    post:
      tags:
        - files
      operationId: createSandboxUpload
      x-boxrouter-switch: sandbox_file_transfers
      x-speakeasy-group: files
      x-speakeasy-name-override: createUpload
      summary: Create a sandbox file upload
      description: |
        You can rely on:
        - A completed upload leaves exactly the declared bytes at the path; a body of another length is refused.

        You can set: path, size.

        Not promised: removing bytes already written when an upload is aborted.

        Starts an upload of a file of known size to an absolute path in a running sandbox. Send
        every returned part as described by its `TransferPart`, then call `completeSandboxUpload`
        with each part's response `ETag`, if any. BoxRouter chooses the `route`; it is
        informational and clients must not branch on it. Upload links and the session last up to
        six hours after creation; `expires_at` says when they end. Launch behaviour is always
        exactly one part. Idempotency-Key is required on create, complete and abort; repeat an
        uncertain call with the same key.

        Needs the sandbox file transfer switch. While the switch is off, which is the default, the
        call is refused with `422 unsupported_feature`. Inline file reads and writes are not affected.
      parameters:
        - $ref: "#/components/parameters/TransferIdempotencyKey"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateSandboxUpload"
            example:
              path: /workspace/data.bin
              size: 1048576
      responses:
        "201":
          description: Created upload
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SandboxUpload"
        "400":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        "409":
          $ref: "#/components/responses/Error"
        "413":
          $ref: "#/components/responses/Error"
        "422":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /sandboxes/{sandbox_id}/files/uploads/{upload_id}/complete:
    parameters:
      - $ref: "#/components/parameters/SandboxId"
      - $ref: "#/components/parameters/SandboxUploadId"
    post:
      tags:
        - files
      operationId: completeSandboxUpload
      x-boxrouter-switch: sandbox_file_transfers
      x-speakeasy-group: files
      x-speakeasy-name-override: completeUpload
      summary: Complete a sandbox file upload
      description: |
        You can rely on:
        - A completed upload leaves exactly the declared bytes at the path; a body of another length is refused.

        You can set: path, size.

        Not promised: removing bytes already written when an upload is aborted.

        Supply each part number exactly once with the `ETag` header returned by its request, if
        it returned one. Repeating the call with the same idempotency key returns the same result.

        Needs the sandbox file transfer switch. While the switch is off, which is the default, the
        call is refused with `422 unsupported_feature`. Inline file reads and writes are not affected.
      parameters:
        - $ref: "#/components/parameters/TransferIdempotencyKey"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CompleteSandboxUpload"
            example:
              parts:
                - number: 1
                  etag: '"9b2cf535f27731c974343645a3985328"'
      responses:
        "200":
          description: Completed file
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CompletedSandboxUpload"
        "400":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        "409":
          $ref: "#/components/responses/Error"
        "422":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /sandboxes/{sandbox_id}/files/uploads/{upload_id}:
    parameters:
      - $ref: "#/components/parameters/SandboxId"
      - $ref: "#/components/parameters/SandboxUploadId"
    delete:
      tags:
        - files
      operationId: abortSandboxUpload
      x-boxrouter-switch: sandbox_file_transfers
      x-speakeasy-group: files
      x-speakeasy-name-override: abortUpload
      summary: Abort a sandbox file upload
      description: |
        You can rely on:
        - A completed upload leaves exactly the declared bytes at the path; a body of another length is refused.

        You can set: path, size.

        Not promised: removing bytes already written when an upload is aborted.

        Idempotent.

        Needs the sandbox file transfer switch. While the switch is off, which is the default, the
        call is refused with `422 unsupported_feature`. Inline file reads and writes are not affected.
      parameters:
        - $ref: "#/components/parameters/TransferIdempotencyKey"
      responses:
        "204":
          description: Upload aborted. Repeating an abort is safe.
        "404":
          $ref: "#/components/responses/Error"
        "422":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /sandboxes/{sandbox_id}/files/downloads:
    parameters:
      - $ref: "#/components/parameters/SandboxId"
    post:
      tags:
        - files
      operationId: createSandboxDownload
      x-boxrouter-switch: sandbox_file_transfers
      x-speakeasy-group: files
      x-speakeasy-name-override: createDownload
      summary: Create a sandbox file download
      description: |
        You can rely on:
        - A complete download returns exactly the file's bytes; a transfer that ends short fails instead of returning a prefix.

        You can set: path.

        Not promised: the file's size before the download starts.

        Creates a temporary download link for a file in a running sandbox. The link lasts up to
        one hour after creation; `expires_at` says when it ends. `route` is informational and
        clients must not branch on it.

        Needs the sandbox file transfer switch. While the switch is off, which is the default, the
        call is refused with `422 unsupported_feature`. Inline file reads and writes are not affected.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateSandboxDownload"
            example:
              path: /workspace/results.json
      responses:
        "201":
          description: Created download; `content` carries the body inline when `inline_below` admits it, otherwise the link fields do
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SandboxDownload"
        "400":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        "409":
          $ref: "#/components/responses/Error"
        "413":
          $ref: "#/components/responses/Error"
        "422":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /sandboxes/{sandbox_id}/files/directories:
    parameters:
      - $ref: "#/components/parameters/SandboxId"
    get:
      tags:
        - files
      operationId: listDirectory
      x-speakeasy-group: files
      x-speakeasy-name-override: list
      summary: List the immediate entries of a directory
      description: |
        You can rely on:
        - Returns every immediate entry, including hidden ones and broken symbolic links, sorted by name.
        - Each entry has its type (file, directory, symlink, other); a link is not followed.
        - More entries than the advertised limit fail; the list is never truncated.

        You can set: path.

        Not promised: metadata beyond the type; a consistent snapshot while the directory changes.

        An unreadable or missing directory fails; it does not appear empty. No recursion.
      parameters:
        - $ref: "#/components/parameters/Path"
      responses:
        "200":
          description: Every immediate entry, sorted by name; an empty directory returns an empty array.
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/DirectoryEntry"
        "400":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        "409":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
    post:
      tags:
        - files
      operationId: createDirectory
      x-speakeasy-group: files
      x-speakeasy-name-override: mkdir
      summary: Create a directory
      description: |
        You can rely on:
        - parents=true (the default) creates missing parents and accepts an existing directory.
        - parents=false requires the parent and refuses an existing path.

        You can set: path, parents.

        Not promised: mode and owner beyond command-user usability.

        A failure may leave some parent directories created. An uncertain mutation is never automatically replayed.
      parameters:
        - $ref: "#/components/parameters/IdempotencyKey"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateDirectoryRequest"
            example:
              path: /workspace/output
              parents: true
      responses:
        "204":
          description: Directory creation completed.
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
        "400":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        "409":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /sandboxes/{sandbox_id}/files/exists:
    parameters:
      - $ref: "#/components/parameters/SandboxId"
    get:
      tags:
        - files
      operationId: pathExists
      x-speakeasy-group: files
      x-speakeasy-name-override: exists
      summary: Check for an accessible filesystem target
      description: |
        You can rely on:
        - Follows symbolic links. True when the target exists; false when it is missing, the link is broken, or the target is not accessible.
        - A failure to check raises an error; it never reads as false.

        You can set: path.

        Not promised: whether the answer still holds after the call.

        Transport and command-startup failures remain errors. This read does not reserve the path or guarantee it remains present.
      parameters:
        - $ref: "#/components/parameters/Path"
      responses:
        "200":
          description: Whether the accessible target exists.
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
          content:
            application/json:
              schema:
                type: boolean
        "400":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        "409":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /sandboxes/{sandbox_id}/files/remove:
    parameters:
      - $ref: "#/components/parameters/SandboxId"
    post:
      tags:
        - files
      operationId: removePath
      x-speakeasy-group: files
      x-speakeasy-name-override: remove
      summary: Remove a file, symbolic link or directory
      description: |
        You can rely on:
        - Removes only the named entry.
        - A nonempty directory needs recursive.
        - A symbolic link, broken or not, is unlinked and its target kept.
        - A missing path fails.
        - The sandbox root is refused.

        You can set: path, recursive.

        Not promised: atomicity; partial progress after a failure.

        A final slash does not change which entry is removed. Failures may leave partial changes. An uncertain mutation is never automatically replayed.
      parameters:
        - $ref: "#/components/parameters/IdempotencyKey"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/RemovePathRequest"
            example:
              path: /workspace/output
              recursive: true
      responses:
        "204":
          description: Removal completed.
          headers:
            Server-Timing:
              $ref: "#/components/headers/ServerTiming"
        "400":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        "409":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /price-versions:
    get:
      tags:
        - pricing
      operationId: listPriceVersions
      x-speakeasy-group: pricing
      x-speakeasy-name-override: versions
      summary: List the providers' live price versions
      description: |
        Public: needs no key or session. Lists every live price version, which is what
        BoxRouter used to calculate sandbox costs, so a published cost can be
        rechecked without BoxRouter's database. Live versions never change. With `at`, returns
        only the version in effect at that time, one per provider; a provider with no version
        in effect then is left out. Effective periods include `effective_from` and exclude
        `effective_to` (`[effective_from, effective_to)`); the version taking over at a boundary
        applies at that instant.
      security: []
      parameters:
        - name: provider
          in: query
          description: Only this provider's versions.
          schema:
            type: string
            minLength: 1
            maxLength: 64
            pattern: ^[a-z][a-z0-9-]*$
        - name: at
          in: query
          description: Only the version in effect at this time.
          schema:
            type: string
            format: date-time
      responses:
        "200":
          description: Live price versions.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PriceVersionPage"
              examples:
                e2b:
                  summary: E2B's version, from its pricing page
                  value:
                    data:
                      - provider: e2b
                        version: e2b@2026-09-29
                        effective_from: 2026-09-29T00:00:00Z
                        effective_to: null
                        last_confirmed_at: 2026-10-03T06:14:09Z
                        document:
                          provider: e2b
                          version: e2b@2026-09-29
                          effective_from: 2026-09-29T00:00:00Z
                          retrieved_at: 2026-09-29T22:52:36Z
                          boundary:
                            start: provider
                            end: provider
                            checked: false
                            evidence: null
                          sources:
                            - url: https://e2b.dev/pricing
                              quote: We charge per second of a running sandbox.
                            - url: https://e2b.dev/pricing
                              quote: vCPUs Plan Costs 1 Hobby / Pro $0.000014/s 2 [Default] Hobby / Pro $0.000028/s 4 Hobby / Pro $0.000056/s 6 Hobby / Pro $0.000084/s 8 Hobby / Pro $0.000112/s
                            - url: https://e2b.dev/pricing
                              quote: Memory (GiB) Plan Costs 1 Hobby / Pro $0.0000045/s 2 Hobby / Pro $0.0000090/s 4 [Default] Hobby / Pro $0.0000180/s 8 Hobby / Pro $0.0000360/s
                            - url: https://e2b.dev/pricing
                              quote: Storage (GiB) Plan Costs 10 Hobby Free 20 Pro Free
                            - url: https://e2b.dev/pricing
                              quote: Hobby FREE /$100 of usage in credits + Usage Costs
                            - url: https://e2b.dev/pricing
                              quote: pro $150 /month + Usage Costs
                            - url: https://e2b.dev/pricing
                              quote: Pro+ — 600 concurrent +$500/mo Pro++ — 1,100 concurrent +$1,000/mo
                            - url: https://e2b.dev/pricing
                              quote: Enterprise requires $3,000/mo minimum.
                            - url: https://docs.e2b.dev/billing
                              quote: You pay per second for compute resources while your sandbox is running.
                            - url: https://docs.e2b.dev/billing
                              quote: Do I pay for stopped sandboxes? No. You only pay while a sandbox is actively running. Once a sandbox is paused, killed or times out, billing stops immediately.
                          classes:
                            - name: default
                              default: true
                              rates:
                                cpu:
                                  usd: "0.000014"
                                  per: vcpu-second
                                memory:
                                  usd: "0.0000045"
                                  per: gib-second
                              meter:
                                cpu:
                                  basis: allocated
                                  billed_states:
                                    - running
                                memory:
                                  basis: allocated
                                  billed_states:
                                    - running
                          exclusions:
                            - "Plan fees: Hobby $0 a month, Pro $150 a month, Enterprise custom with a $3,000 a month minimum."
                            - "Concurrency add-ons on Pro: Pro+ (600 concurrent sandboxes) $500 a month, Pro++ (1,100) $1,000 a month."
                            - "Credits: Hobby includes a one-time $100 usage credit, which list prices do not apply."
                            - "Storage: free, 10 GiB on Hobby and 20 GiB on Pro; no separate storage rate is published."
                          open_questions:
                            - The pages never say in words whether the meter is allocated or used CPU and RAM; both price tables are keyed by configured size, so allocation is assumed.
                            - Charges for egress, template builds and template storage are not published anywhere read; absence is not confirmation that they are free.
        "400":
          $ref: "#/components/responses/Error"
        "429":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
  /providers:
    get:
      tags:
        - providers
      operationId: listProviders
      x-speakeasy-group: providers
      x-speakeasy-name-override: list
      summary: List available provider routes
      description: |
        Returns only supply visible to the caller, including account-specific
        entitlements and limits. This is a point-in-time eligibility view, not a
        capacity reservation. Unknown or unverified capability guarantees are not
        advertised as supported. Server-side eligibility is rechecked at creation.
      responses:
        "200":
          description: Routes available to this workspace.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProviderPage"
        "401":
          $ref: "#/components/responses/Error"
        default:
          $ref: "#/components/responses/Error"
components:
  securitySchemes:
    BrowserSession:
      type: apiKey
      in: cookie
      name: __Host-boxrouter_session
      description: HttpOnly server-side session. Local HTTP uses boxrouter_session. Browser mutations require the session CSRF token and the configured Origin; execution API keys cannot administer sessions or customer keys.
    BoxRouterKey:
      type: http
      scheme: bearer
      description: BoxRouter API key; never an upstream provider key.
    GitHubWebhookSignature:
      type: apiKey
      in: header
      name: X-Hub-Signature-256
      description: HMAC-SHA256 signature from the shared GitHub App; verify against the original raw request bytes before JSON parsing.
  schemas:
    AgentPage:
      type: object
      additionalProperties: false
      required:
        - agents
      properties:
        agents:
          type: array
          items:
            $ref: "#/components/schemas/Agent"
    Agent:
      type: object
      additionalProperties: false
      required:
        - name
        - display_name
        - description
        - secrets
        - ports
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 63
          pattern: ^[a-z][a-z0-9-]{0,62}$
        display_name:
          type: string
          minLength: 1
          maxLength: 128
          description: The agent's human-facing name.
        description:
          type:
            - string
            - "null"
        secrets:
          type: array
          items:
            $ref: "#/components/schemas/SecretName"
        interactive:
          type: boolean
          default: true
        preinstalled:
          type: boolean
          default: false
          description: The launch runs on an image that already carries this agent's harness.
        required_env:
          $ref: "#/components/schemas/AgentRequiredEnvironment"
        max_copies:
          $ref: "#/components/schemas/AgentMaximumCopies"
        model_access:
          type: array
          items:
            $ref: "#/components/schemas/ModelAccess"
        sign_in:
          type: string
          minLength: 1
          maxLength: 256
        ports:
          type: array
          items:
            type: integer
            minimum: 1
            maximum: 65535
    SecretName:
      type: string
      minLength: 1
      maxLength: 128
      pattern: ^[A-Z][A-Z0-9_]{0,127}$
    AgentRequiredEnvironment:
      type: array
      default: []
      items:
        $ref: "#/components/schemas/AgentEnvironmentRequirement"
      description: Names of environment variables that must have nonblank values when launching the agent. BoxRouter checks them after merging env overrides and resolving credentials, before reservation. Values remain unchanged; this does not verify an external session or credential.
    AgentEnvironmentRequirement:
      type: object
      additionalProperties: false
      required:
        - name
      properties:
        name:
          type: string
          pattern: ^[A-Za-z_][A-Za-z0-9_]*$
        description:
          type: string
          minLength: 1
          maxLength: 1024
          description: Plain-text setup guidance, never an input value or executable markup.
    AgentMaximumCopies:
      type: integer
      minimum: 1
      description: Maximum copies of this resolved manifest in one launch run. Omission adds no manifest-specific ceiling. Interactive manifests remain ineligible for launch runs.
    ModelAccess:
      type: object
      additionalProperties: false
      required:
        - secret
        - provider
        - label
      properties:
        secret:
          $ref: "#/components/schemas/SecretName"
        provider:
          type: string
          enum:
            - openai
            - anthropic
            - openrouter
            - cursor
          x-boxrouter-open-enum: true
        label:
          type: string
          minLength: 1
          maxLength: 128
    ApiErrorResponse:
      type: object
      required:
        - error
      description: "The one error body every failing call answers. It leads with what to do: `retry` says whether to send the call again, `fix` says what to change, `outcome` says what happened, and `doc_url` links to the page that explains the code. Clients retry `retry: safe` for you with the same idempotency key."
      x-sandbox-provider-spec-codes:
        - no_eligible_route
        - knob_not_named
        - unrecognised_word
        - invalid_request
        - not_named
        - gate_unavailable
        - account_unhealthy
        - lifetime_limit
        - lifetime_used_up
        - not_running
        - invalid_state
        - sandbox_ended
        - not_found
        - provider_error
        - timeout
        - contract_violation
      properties:
        error:
          type: object
          required:
            - code
            - message
            - request_id
            - outcome
            - retry
            - retry_after_seconds
          properties:
            code:
              type: string
              description: |
                Stable machine category. Operation failures use SandboxProviderSpec error codes,
                including invalid_request, invalid_state, not_found, unauthorized,
                incompatible_requirements, no_eligible_route, capacity_unavailable,
                capability_unavailable, contract_violation, resource_limit, timeout and
                provider_error. Hosted service failures add method_not_allowed,
                unauthenticated, forbidden, actions_sandbox_protected,
                insufficient_credit, spend_limit_exceeded,
                provider_not_enabled, credentials_rejected, conflict, rate_limited, provider_quota, request_timeout, overloaded,
                database_unavailable, payment_method_required, payment_declined, promotion_already_redeemed, account_exists, and internal_error. unsupported_feature and
                output_limit are earlier names still returned by some routes.
                Organization and membership operations also use organization_changed, last_admin,
                stale_membership, directory_managed and change_in_progress. Key ownership and
                invitation operations use idempotency_key_reused and conflict.
                Credit refunds and purchase quotes also use refund_window_closed,
                nothing_to_refund, refund_below_minimum, refund_in_progress,
                purchase_in_progress, dispute_open, already_refunded,
                refund_amount_changed, refund_fee_unavailable, quote_changed,
                session_required, refund_daily_limit, refund_rate_limit,
                refund_retry_requires_support, idempotency_key_reused and fraud_review.
                Card setup adds card_setup_rate_limit (429, with Retry-After): a workspace may start five
                card setups an hour.
                A provider that refuses the request for its own plan or quota limit answers
                429 provider_quota with the provider's words in native_message; rate_limited is
                BoxRouter's own limit.
                Routing Prevent Overrides refuses a request containing preferences.order,
                preferences.sort or preferences.allow_fallbacks with routing_overrides_forbidden,
                outcome not_started and details.fields naming the prohibited inputs. No provider
                effect occurs.
                Launch operations also use invalid_request, secret_missing, agent_not_found,
                no_eligible_route and launch_failed.
                A process or terminal is observed by one API task. When that task stops, process
                output, result and stdin answer 410 process_observer_lost (reading or cancelling the
                process still works), and a terminal on a provider that cannot reattach it answers
                410 terminal_observer_lost. invalid_observer_hop (403) refuses an internal hop
                between API tasks that is not signed; clients never send one.
                Opening a preview that the provider answered with other access than the request
                asked for returns 502 preview_access_refused: that preview is never returned, and
                details say whether it was ended.
                Starting a process with wait_for_port answers 409 port_not_listening, outcome partial,
                when the process exits before the port accepts connections or the port stays closed for
                30 seconds; the process is ended and details carry the output so far.
                A request that must store content (a volume, a saved artifact) under transient content
                retention answers 409 privacy_persistence_conflict with outcome not_started.
                A sandbox request that stops before anything is created also answers
                lifetime_cap_exceeded, knob_not_named and unrecognised_word (400), and price_unknown and
                policy_denied (409), each with outcome not_started; the Actions codes job_claim_lost,
                actions_authority_changed, actions_candidate_ceiling_withdrawn and
                actions_candidate_outside_ceiling (409) say the job's claim, settings or admitted routes
                changed; provider_budget_exhausted (409) says a run's provider budget allows no
                further create. A create that names an image Cloud cannot use answers 400
                image_architecture_unsupported, image_digest_resolution_failed,
                image_preparation_unsupported or private_image_managed_route_unavailable, and
                privacy_admission_unavailable when privacy cannot be enforced; each says what to change.
                Launch and source-repository refusals name the repository or manifest fault (code_ref_not_found,
                code_repository_*, source_*, invalid_source_manifest, invalid_manifest_encoding,
                invalid_run_spec, repository_*, reserved_env_refused, launch_run_position_conflict,
                code_reference_moved, dockerfile_context_unsupported, image_tag_unresolved,
                recovery_sandbox_mismatch); the GitHub-side ones (source_rate_limited,
                source_resolution_failed, source_resolution_timeout, code_repository_resolution_failed)
                are safe to resend. image_setup_timeout, managed_funding_unavailable,
                lifetime_funding_conflict, candidate_admission_unavailable, secrets_unavailable and the
                Actions candidate codes (actions_candidate_record_invalid, actions_create_budget_exhausted,
                actions_create_format_mismatch) complete the creation and extension refusals.
                dispatch_limit_reached, inline_secret_values_refused and launch_incomplete come from
                launch runs, provider_policy_unknown from an operation whose privacy evidence cannot be
                verified, and quota_exceeded from a full managed provider account.
                Volume operations also use volume_busy, volume_lease_lost, volume_not_attached,
                volume_incompatible, volume_save_failed and volume_restore_failed. Creating a
                sandbox on a volume whose writer is ending waits for the release (at most 120
                seconds) before it can answer volume_busy.
                A team member's new personal API key is paused until WorkOS membership sync catches
                up: it answers 403 forbidden whose message and fix say the key becomes active once
                the team membership syncs, usually within a minute, and the call can be retried.
                Clients must preserve and handle unknown categories explicitly.
              x-boxrouter-error-codes:
                account_exists:
                  - 409
                account_unhealthy:
                  - 402
                actions_authority_changed:
                  - 409
                actions_candidate_ceiling_withdrawn:
                  - 409
                actions_candidate_outside_ceiling:
                  - 409
                actions_candidate_record_invalid:
                  - 409
                actions_create_budget_exhausted:
                  - 409
                actions_create_format_mismatch:
                  - 409
                actions_sandbox_protected:
                  - 403
                agent_not_found:
                  - 404
                already_refunded:
                  - 409
                authorization_error:
                  - 403
                candidate_admission_unavailable:
                  - 503
                capability_unavailable:
                  - 409
                  - 503
                capability_unverified:
                  - 409
                card_setup_rate_limit:
                  - 429
                change_in_progress:
                  - 409
                code_ref_not_found:
                  - 404
                code_reference_moved:
                  - 409
                code_repository_not_authorized:
                  - 403
                code_repository_not_found:
                  - 404
                code_repository_not_private:
                  - 400
                code_repository_not_public:
                  - 404
                code_repository_resolution_failed:
                  - 502
                command_failed:
                  - 409
                command_observation_busy:
                  - 409
                conflict:
                  - 409
                contract_violation:
                  - 409
                credentials_rejected:
                  - 409
                  - 422
                database_unavailable:
                  - 503
                directory_managed:
                  - 409
                dispatch_limit_reached:
                  - 409
                dispute_open:
                  - 409
                dockerfile_context_unsupported:
                  - 400
                expired:
                  - 410
                forbidden:
                  - 403
                fraud_review:
                  - 409
                gate_unavailable:
                  - 409
                idempotency_key_reused:
                  - 409
                  - 422
                identity_unavailable:
                  - 503
                image_architecture_unsupported:
                  - 400
                image_digest_resolution_failed:
                  - 400
                image_preparation_unsupported:
                  - 400
                image_setup_timeout:
                  - 504
                image_tag_unresolved:
                  - 400
                inline_secret_values_refused:
                  - 422
                insufficient_credit:
                  - 402
                internal_error:
                  - 500
                invalid_manifest_encoding:
                  - 400
                invalid_observer_hop:
                  - 403
                invalid_request:
                  - 400
                  - 413
                  - 422
                  - 428
                invalid_run_spec:
                  - 400
                invalid_source_manifest:
                  - 400
                invalid_state:
                  - 409
                job_claim_lost:
                  - 409
                knob_not_named:
                  - 400
                last_admin:
                  - 409
                launch_failed:
                  - 409
                launch_incomplete:
                  - 409
                launch_run_position_conflict:
                  - 409
                lifetime_cap_exceeded:
                  - 400
                lifetime_funding_conflict:
                  - 409
                lifetime_limit:
                  - 409
                lifetime_used_up:
                  - 409
                managed_funding_unavailable:
                  - 503
                method_not_allowed:
                  - 405
                no_eligible_route:
                  - 409
                not_found:
                  - 404
                not_named:
                  - 409
                not_running:
                  - 409
                nothing_to_refund:
                  - 409
                organization_changed:
                  - 409
                output_limit:
                  - 413
                overloaded:
                  - 503
                payment_declined:
                  - 402
                payment_method_required:
                  - 409
                payment_provider_rejected:
                  - 502
                payment_provider_unavailable:
                  - 503
                payments_unavailable:
                  - 503
                policy_denied:
                  - 409
                port_not_listening:
                  - 409
                preview_access_refused:
                  - 502
                price_unknown:
                  - 409
                privacy_admission_unavailable:
                  - 400
                privacy_persistence_conflict:
                  - 409
                private_image_managed_route_unavailable:
                  - 400
                process_observer_lost:
                  - 410
                promotion_already_redeemed:
                  - 409
                promotion_code_invalid:
                  - 404
                provider_budget_exhausted:
                  - 409
                provider_error:
                  - 502
                provider_not_enabled:
                  - 409
                provider_policy_unknown:
                  - 409
                provider_quota:
                  - 429
                purchase_in_progress:
                  - 409
                quota_exceeded:
                  - 429
                quote_changed:
                  - 409
                rate_limited:
                  - 429
                recovery_sandbox_mismatch:
                  - 409
                refund_amount_changed:
                  - 409
                refund_below_minimum:
                  - 409
                refund_daily_limit:
                  - 429
                refund_fee_unavailable:
                  - 409
                refund_in_progress:
                  - 409
                refund_rate_limit:
                  - 429
                refund_retry_requires_support:
                  - 409
                refund_window_closed:
                  - 409
                repository_file_unsupported:
                  - 400
                repository_too_large:
                  - 400
                request_timeout:
                  - 408
                reserved_env_refused:
                  - 400
                  - 422
                resource_limit:
                  - 413
                  - 507
                routing_overrides_forbidden:
                  - 400
                run_limit_exceeded:
                  - 429
                sandbox_ended:
                  - 410
                secret_missing:
                  - 400
                secrets_unavailable:
                  - 503
                session_required:
                  - 403
                source_file_not_found:
                  - 400
                source_file_too_large:
                  - 413
                source_rate_limited:
                  - 503
                source_ref_not_found:
                  - 400
                source_repository_not_accessible:
                  - 404
                source_repository_not_found:
                  - 400
                source_repository_not_private:
                  - 400
                source_repository_not_public:
                  - 400
                source_resolution_failed:
                  - 502
                source_resolution_timeout:
                  - 504
                spend_limit_exceeded:
                  - 402
                stale_membership:
                  - 409
                terminal_closed:
                  - 410
                terminal_observer_lost:
                  - 410
                timeout:
                  - 504
                unauthenticated:
                  - 401
                unavailable:
                  - 503
                unrecognised_word:
                  - 400
                unsupported_feature:
                  - 422
                volume_busy:
                  - 409
                volume_incompatible:
                  - 409
                volume_lease_lost:
                  - 409
                volume_not_attached:
                  - 409
                volume_restore_failed:
                  - 502
                volume_save_failed:
                  - 409
                  - 502
                volume_unavailable:
                  - 409
                wrong_region:
                  - 421
              x-boxrouter-error-retry:
                account_exists: after_fix
                account_unhealthy: after_fix
                actions_authority_changed: after_fix
                actions_candidate_ceiling_withdrawn: after_fix
                actions_candidate_outside_ceiling: after_fix
                actions_candidate_record_invalid: after_fix
                actions_create_budget_exhausted: after_fix
                actions_create_format_mismatch: after_fix
                actions_sandbox_protected: never
                agent_not_found: after_fix
                already_refunded: never
                authorization_error: check_outcome
                candidate_admission_unavailable: never
                capability_unavailable: check_outcome
                capability_unverified: after_fix
                card_setup_rate_limit: safe
                change_in_progress: safe
                code_ref_not_found: after_fix
                code_reference_moved: after_fix
                code_repository_not_authorized: after_fix
                code_repository_not_found: after_fix
                code_repository_not_private: after_fix
                code_repository_not_public: after_fix
                code_repository_resolution_failed: safe
                command_failed: after_fix
                command_observation_busy: safe
                conflict: check_outcome
                contract_violation: check_outcome
                credentials_rejected: after_fix
                database_unavailable: check_outcome
                directory_managed: never
                dispatch_limit_reached: after_fix
                dispute_open: after_fix
                dockerfile_context_unsupported: after_fix
                expired: check_outcome
                forbidden: after_fix
                fraud_review: safe
                gate_unavailable: after_fix
                idempotency_key_reused: after_fix
                identity_unavailable: safe
                image_architecture_unsupported: after_fix
                image_digest_resolution_failed: after_fix
                image_preparation_unsupported: after_fix
                image_setup_timeout: after_fix
                image_tag_unresolved: after_fix
                inline_secret_values_refused: after_fix
                insufficient_credit: after_fix
                internal_error: never
                invalid_manifest_encoding: after_fix
                invalid_observer_hop: after_fix
                invalid_request: after_fix
                invalid_run_spec: after_fix
                invalid_source_manifest: after_fix
                invalid_state: check_outcome
                job_claim_lost: after_fix
                knob_not_named: after_fix
                last_admin: after_fix
                launch_failed: check_outcome
                launch_incomplete: check_outcome
                launch_run_position_conflict: after_fix
                lifetime_cap_exceeded: after_fix
                lifetime_funding_conflict: after_fix
                lifetime_limit: check_outcome
                lifetime_used_up: after_fix
                managed_funding_unavailable: safe
                method_not_allowed: after_fix
                no_eligible_route: after_fix
                not_found: check_outcome
                not_named: after_fix
                not_running: after_fix
                nothing_to_refund: never
                organization_changed: safe
                output_limit: check_outcome
                overloaded: safe
                payment_declined: after_fix
                payment_method_required: after_fix
                payment_provider_rejected: check_outcome
                payment_provider_unavailable: check_outcome
                payments_unavailable: safe
                policy_denied: after_fix
                port_not_listening: after_fix
                preview_access_refused: never
                price_unknown: after_fix
                privacy_admission_unavailable: after_fix
                privacy_persistence_conflict: after_fix
                private_image_managed_route_unavailable: after_fix
                process_observer_lost: check_outcome
                promotion_already_redeemed: never
                promotion_code_invalid: after_fix
                provider_budget_exhausted: never
                provider_error: check_outcome
                provider_not_enabled: after_fix
                provider_policy_unknown: after_fix
                provider_quota: after_fix
                purchase_in_progress: safe
                quota_exceeded: safe
                quote_changed: after_fix
                rate_limited: safe
                recovery_sandbox_mismatch: check_outcome
                refund_amount_changed: after_fix
                refund_below_minimum: never
                refund_daily_limit: safe
                refund_fee_unavailable: safe
                refund_in_progress: safe
                refund_rate_limit: safe
                refund_retry_requires_support: never
                refund_window_closed: never
                repository_file_unsupported: after_fix
                repository_too_large: after_fix
                request_timeout: safe
                reserved_env_refused: after_fix
                resource_limit: check_outcome
                routing_overrides_forbidden: after_fix
                run_limit_exceeded: after_fix
                sandbox_ended: check_outcome
                secret_missing: after_fix
                secrets_unavailable: never
                session_required: never
                source_file_not_found: after_fix
                source_file_too_large: after_fix
                source_rate_limited: safe
                source_ref_not_found: after_fix
                source_repository_not_accessible: after_fix
                source_repository_not_found: after_fix
                source_repository_not_private: after_fix
                source_repository_not_public: after_fix
                source_resolution_failed: safe
                source_resolution_timeout: safe
                spend_limit_exceeded: after_fix
                stale_membership: after_fix
                terminal_closed: after_fix
                terminal_observer_lost: after_fix
                timeout: check_outcome
                unauthenticated: after_fix
                unavailable: safe
                unrecognised_word: after_fix
                unsupported_feature: after_fix
                volume_busy: after_fix
                volume_incompatible: after_fix
                volume_lease_lost: after_fix
                volume_not_attached: after_fix
                volume_restore_failed: check_outcome
                volume_save_failed: check_outcome
                volume_unavailable: after_fix
                wrong_region: after_fix
            message:
              type: string
            details:
              type: object
              description: |
                Code-specific context, spelled for programs. no_eligible_route carries considered,
                excluded, closest and nearest; knob_not_named the knob and its values;
                unrecognised_word the path, the word and the known ones; not_named the gate, what
                the call needed beyond the grant and the corrected requirements line; gate_unavailable
                the gate and account; lifetime_limit the ceiling and this run. A creation that
                stopped before a sandbox existed may also carry `fallbacks_allowed`, `stopped_reason`
                and `attempts`, spelled and bounded as in the sandbox route selection.
                capability_unavailable may identify the needed capability. A launch preparation failure can follow sandbox creation;
                inspect outcome and cleanup details before creating another. Clients must
                preserve fields they do not know.
              properties:
                selection_version:
                  type: integer
                  minimum: 0
                  description: Current selected organization version for organization_changed.
                considered:
                  type: integer
                  minimum: 0
                  description: no_eligible_route only; how many routes were considered.
                excluded:
                  type: array
                  description: no_eligible_route only; every considered route that cannot serve the request. Each entry names its `provider` and the checks it failed (`failed` for a sandbox request, `reasons` and `exclusions` for a launch); clients must preserve fields they do not know.
                  items:
                    type: object
                    required:
                      - provider
                    properties:
                      provider:
                        type: string
                closest:
                  type:
                    - object
                    - "null"
                  description: no_eligible_route only; the excluded route that fails fewest checks, or null.
                nearest:
                  type: array
                  description: no_eligible_route only; the smallest changes that would make a route eligible, nearest first. An entry names the change (`change` and `then_eligible` for a sandbox request, `provider`, `distance` and `failing` for a launch); clients must preserve fields they do not know.
                  items:
                    type: object
                requirement:
                  type: string
                  description: The SandboxProviderSpec family whose requirement would make this operation available.
                purchase_id:
                  type: string
                  description: Retained failed credit purchase ID, when a saved-card charge was declined.
                preview:
                  $ref: "#/components/schemas/CreditRefundPreview"
                  description: Current refund preview returned with 409 refund_amount_changed.
                quote:
                  $ref: "#/components/schemas/CreditPurchaseQuote"
                  description: Current purchase quote returned with 409 quote_changed.
                redemption:
                  $ref: "#/components/schemas/CreditPromotion"
                  description: Original promotion redemption returned with 409 promotion_already_redeemed.
                scope:
                  type: string
                  enum:
                    - workspace
                  description: spend_limit_exceeded only; which configured cap refused admission.
                period:
                  type: string
                  enum:
                    - total
                    - daily
                    - monthly
                  description: spend_limit_exceeded only; the cap period that applies.
                limit_nanodollars:
                  type: integer
                  minimum: 1
                  description: spend_limit_exceeded only; exact configured USD nanodollars.
                requested_nanodollars:
                  type: integer
                  minimum: 1
                  description: spend_limit_exceeded only; exact proposed managed hold.
                sign_in_method:
                  type: string
                  description: "account_exists only: how the existing account with this verified address signs in (google, github, email or other)."
                masked_email:
                  type: string
                  description: "account_exists only: that account's address, masked as `t…@gmail.com`."
                failure_reason:
                  type: string
                  maxLength: 100
                  description: Short safe card-decline reason; raw Stripe messages are not returned.
                names:
                  type: array
                  uniqueItems: true
                  items:
                    $ref: "#/components/schemas/SecretName"
                  description: All manifest secret names that could not be resolved.
                fields:
                  type: array
                  uniqueItems: true
                  items:
                    type: string
                    enum:
                      - preferences.order
                      - preferences.sort
                      - preferences.allow_fallbacks
                  description: routing_overrides_forbidden only; the prohibited request inputs.
                volume_id:
                  type: string
                  description: BoxRouter Volume ID involved in the error.
                writer_sandbox_id:
                  type: string
                  description: volume_busy only; the sandbox that holds the volume's writer lease, when it is a recorded sandbox.
                checkpoint:
                  type: integer
                  minimum: 1
                  description: Volume checkpoint involved in the error.
                limit_bytes:
                  type: integer
                  minimum: 0
                observed_bytes:
                  type: integer
                  minimum: 0
                mismatch:
                  type: object
                  additionalProperties: false
                  required:
                    - field
                    - recorded
                    - requested
                  properties:
                    field:
                      type: string
                      enum:
                        - image
                        - arch
                    recorded:
                      type: string
                    requested:
                      type: string
                volume_phase:
                  type: string
                  enum:
                    - check
                    - branch
                    - lease
                    - identity
                    - install
                    - credential
                    - mount
                    - metadata
                    - checkpoint
                    - commit
                    - write
                phase:
                  type: string
                  enum:
                    - code
                    - setup
                    - files
                    - background
                    - previews
                  description: Preparation phase that failed.
                command:
                  type: string
                  description: Command whose nonzero exit stopped launch preparation.
                exit_code:
                  type: integer
                  description: Exit code of the failed command, or of the process that exited before its port listened.
                stderr_tail:
                  type: string
                  description: |
                    At most the final 4 KiB of stderr from the failed command, with resolved secret values
                    redacted. port_not_listening carries the same tail of the process's stderr.
                stdout_tail:
                  type: string
                  description: port_not_listening only; at most the final 4 KiB of the process's stdout.
                agent:
                  type: string
                  description: Unknown catalog agent name.
                port:
                  type: integer
                  minimum: 1
                  maximum: 65535
                  description: The sandbox port whose preview was refused (preview_access_refused) or that never accepted connections (port_not_listening).
                revoked:
                  type: boolean
                  description: |
                    preview_access_refused only; true when the unwanted preview was ended and no
                    access to the port remains. False: it may still be reachable (outcome unknown).
                preview_id:
                  type: string
                  description: |
                    preview_access_refused only; the identifier the provider gave the preview that may
                    still be reachable. Present only for a public preview.
                url:
                  type: string
                  format: uri
                  description: |
                    preview_access_refused only; the address of a public preview that may still be
                    reachable. Never present for a private preview, whose address holds a credential,
                    and never when it was ended.
            request_id:
              type: string
            doc_url:
              type:
                - string
                - "null"
              format: uri
              description: "Where the documentation explains this code: `https://boxrouter.ai/docs/error-codes#` followed by the code, for example `https://boxrouter.ai/docs/error-codes#no_eligible_route`. The page has one anchored row per code with what it means and what to do. BoxRouter sends it with every error; it is null for a code that has no documented row. Clients must accept an error body that omits it."
            fix:
              type:
                - string
                - "null"
              minLength: 1
              description: Actionable change for the caller when the request cannot be completed. A refusal (4xx) always carries one; a server or provider fault, including an error event that ends a terminal or process stream, can answer null.
            operation_id:
              type:
                - string
                - "null"
              description: Durable server operation ID for reconciliation after uncertain mutations.
            outcome:
              type: string
              enum:
                - not_started
                - completed
                - partial
                - unknown
              description: |
                What is known about the requested side effect. `not_started` means nothing ran
                anywhere, at BoxRouter or at a provider; a launch-setup or process-start failure
                after dispatch is `partial` or `unknown`. Unknown and partial forbid automatic
                replay and always carry `retry: check_outcome`. An HTTP status alone does not establish
                outcome.
                Many refusals report not_started before the provider is asked anything. A read
                (GET or HEAD) has no side effect to be unsure about, so its failures report
                not_started and carry the code's own retry value. A launch preparation failure
                happens after the sandbox was created and setup commands may already have run, and
                deleting the sandbox does not undo effects outside it: it reports partial once the
                cleanup is confirmed and unknown if it is not. A process that wait_for_port ended
                reports partial. volume_restore_failed on a resume or create,
                and volume_save_failed after the guest was read, report unknown or partial; they
                carry the operation_id when the call was recorded, and their fix does not offer to
                send the call again unchanged.
            retry:
              type: string
              enum:
                - safe
                - after_fix
                - check_outcome
                - never
              x-boxrouter-open-enum: true
              description: |
                What the caller may do next, fixed per code (x-boxrouter-error-retry): safe resends the
                same request, after_fix changes the request or setup first, check_outcome reads
                `outcome` and the resource before resending, never does not resend. Branch on this
                instead of the HTTP status.
            retry_after_seconds:
              type:
                - integer
                - "null"
              minimum: 0
              description: "How long to wait before a `retry: safe` resend, as the Retry-After header says it; null when there is no wait to name."
            provider:
              type:
                - string
                - "null"
            native_code:
              type:
                - string
                - "null"
              description: The provider's machine-readable error code, unchanged, when it reports one.
            native_status:
              type:
                - integer
                - "null"
              description: The provider's HTTP status, unchanged, when the failure came from a provider response.
            native_message:
              type:
                - string
                - "null"
              maxLength: 4096
              description: The provider's own error message when it answered with an HTTP 4xx refusal (for example a plan limit); null otherwise. Capped at 512 characters, with whitespace collapsed. Credential secrets and token-shaped strings are removed before return. The literal `[redacted]` marker is used when all credential secret fields are at least 12 characters; legacy saved secrets shorter than 12 characters are removed without the marker.
    CreditRefundPreview:
      type: object
      additionalProperties: false
      required:
        - unused
        - held_excluded
        - fee
        - principal
        - tax
        - total
        - card
      properties:
        unused:
          type: integer
          minimum: 0
          description: Unused purchased credit in US cents, before held credit is excluded.
        held_excluded:
          type: integer
          minimum: 0
          description: Credit held for running sandboxes, in US cents.
        fee:
          type: integer
          minimum: 0
          description: Original Stripe processing fee retained, in US cents.
        principal:
          type: integer
          minimum: 0
          description: Tax-exclusive principal returned to the original card, in US cents.
        tax:
          type: integer
          minimum: 0
          description: Tax Stripe adds to the credit note, in US cents.
        total:
          type: integer
          minimum: 0
          description: Total Stripe will refund, in US cents.
        card:
          $ref: "#/components/schemas/MaskedCreditCard"
    MaskedCreditCard:
      type: object
      additionalProperties: false
      properties:
        brand:
          type:
            - string
            - "null"
          description: Card brand; absent for Link methods.
        last4:
          type:
            - string
            - "null"
          pattern: ^[0-9]{4}$
          description: Last four card digits; absent for Link methods.
        type:
          type: string
          enum:
            - card
            - link
          x-boxrouter-open-enum: true
        email:
          type: string
          format: email
          description: Link account email; present only when type is link.
      allOf:
        - if:
            properties:
              type:
                const: link
            required:
              - type
          then:
            required:
              - email
            properties:
              brand:
                type: "null"
              last4:
                type: "null"
        - if:
            properties:
              type:
                const: card
            required:
              - type
          then:
            required:
              - brand
              - last4
            properties:
              brand:
                type: string
              last4:
                type: string
                pattern: ^[0-9]{4}$
    CreditPurchaseQuote:
      type: object
      additionalProperties: false
      required:
        - amount
        - tax
        - total
        - address_required
      properties:
        amount:
          type: integer
          minimum: 1
          description: Tax-exclusive credit amount in US cents; this is what is added to the balance.
        fee_minor:
          type: integer
          minimum: 0
          description: BoxRouter's purchase fee in US cents, computed by the server and charged on top of amount. Tax applies to amount plus fee. Absent on older services.
        tax:
          type: integer
          minimum: 0
          description: Estimated tax in US cents, on the credits and the fee.
        total:
          type: integer
          minimum: 1
          description: "Estimated total to charge in US cents: amount plus fee_minor plus tax."
        address_required:
          type: boolean
        missing:
          type: array
          items:
            type: string
          description: Required billing address fields missing for an exact tax quote.
    CreditPromotion:
      type: object
      additionalProperties: false
      required:
        - code
        - amount_nanodollars
        - description
        - redeemed_at
      properties:
        code:
          type: string
          description: The code in its canonical upper-case form.
        amount_nanodollars:
          type: string
          maxLength: 19
          pattern: ^(?:[1-9][0-9]{0,17}|[1-8][0-9]{18}|9[0-1][0-9]{17}|92[0-1][0-9]{16}|922[0-2][0-9]{15}|9223[0-2][0-9]{14}|92233[0-6][0-9]{13}|922337[0-1][0-9]{12}|92233720[0-2][0-9]{10}|922337203[0-5][0-9]{9}|9223372036[0-7][0-9]{8}|92233720368[0-4][0-9]{7}|922337203685[0-3][0-9]{6}|9223372036854[0-6][0-9]{5}|92233720368547[0-6][0-9]{4}|922337203685477[0-4][0-9]{3}|9223372036854775[0-7][0-9]{2}|922337203685477580[0-7])$
          description: Credit granted, in billionths of a US dollar, as a canonical positive signed 64-bit nanodollar string.
        description:
          type: string
          description: What the credit is for, as the promotion describes it.
        redeemed_at:
          type: string
          format: date-time
    AgentDetails:
      type: object
      additionalProperties: false
      required:
        - name
        - display_name
        - description
        - manifest
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 63
          pattern: ^[a-z][a-z0-9-]{0,62}$
        display_name:
          type: string
          minLength: 1
          maxLength: 128
          description: The agent's human-facing name.
        description:
          type:
            - string
            - "null"
        manifest:
          $ref: "#/components/schemas/AgentManifest"
    AgentManifest:
      type: object
      additionalProperties: false
      required:
        - name
        - command
      description: |
        A reusable agent environment and command. A field this schema does not list is refused
        with 400 before the launch is accepted. interactive defaults to true. When false, launch
        starts the command unattended after preparation.
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 63
          pattern: ^[a-z][a-z0-9-]{0,62}$
          description: A lowercase identifier for the agent.
        description:
          type: string
          description: Optional text describing the agent.
        image:
          oneOf:
            - type: string
              minLength: 1
              description: OCI image reference.
            - $ref: "#/components/schemas/DockerfileBuild"
            - type: object
              additionalProperties: false
              required:
                - private_oci
              properties:
                private_oci:
                  type: string
                  minLength: 1
                  description: OCI image reference in a registry that needs a login; send registry_credentials with the launch request.
          description: |
            Omit to use the standard image. {private_oci} names an image whose registry needs a
            login, sent as `registry_credentials` on the launch request; it launches only on
            routes that use your own provider keys, and not as an unattended run.
        setup:
          type: array
          items:
            type: string
            pattern: ^[^\u0000]*$
          description: Shell commands run in order before files and background commands.
        command:
          type: string
          minLength: 1
          pattern: ^[^\u0000]*$
          description: Command to run in an attached terminal or as an unattended process after preparation succeeds.
        env:
          $ref: "#/components/schemas/LaunchEnvironment"
          description: Non-secret environment values applied to setup and the launch.
        secrets:
          type: array
          uniqueItems: true
          items:
            $ref: "#/components/schemas/SecretName"
          description: Names whose values are resolved by the server and added only to the create environment.
        required_env:
          $ref: "#/components/schemas/AgentRequiredEnvironment"
        max_copies:
          $ref: "#/components/schemas/AgentMaximumCopies"
        model_access:
          type: array
          items:
            $ref: "#/components/schemas/ModelAccess"
          description: Named secrets that configure this catalog agent for model access. A launch may select at most one.
        sign_in:
          type: string
          minLength: 1
          maxLength: 256
          description: What to do inside the agent when no recognized model secret is selected.
        files:
          type: object
          propertyNames:
            pattern: ^(?:~/)?(?!/)(?!~/?$)(?!.*(?:^|/)\.\.(?:/|$))(?!.*(?:^|/)\.(?:/|$))(?!.*\/$)[^\u0000]+$
          additionalProperties:
            oneOf:
              - type: string
              - type: object
          description: Relative sandbox paths mapped to UTF-8 text or JSON objects, written before background commands.
        background:
          type: array
          items:
            type: string
            pattern: ^[^\u0000]*$
          description: Shell commands started detached during preparation before the launch command starts.
        ports:
          type: array
          uniqueItems: true
          items:
            type: integer
            minimum: 1
            maximum: 65535
          description: Ports to expose as previews. Naming ports adds the previews requirement to routing.
        interactive:
          type: boolean
          default: true
          description: When true, a client can attach a terminal; when false, launch starts the command unattended.
        preinstalled:
          type: boolean
          default: false
          description: Declares that the launch's image already carries this agent's harness. The catalog sets it for agents bundled in the agents image; the listAgents entry reports it.
        resources:
          $ref: "#/components/schemas/SandboxProviderSpecResources"
        lifetime_seconds:
          type:
            - integer
            - "null"
          minimum: 1
          description: "Requested sandbox lifetime in seconds for this launch; the sandbox may run past its lifetime deadline. Null asks for a sandbox that never ends. Only a provider that never ends a sandbox serves it, and only on the workspace's own provider account. Left out, the create's own default applies: rolling credit reservations on a credit-funded route that can extend a lifetime, else 300 seconds."
        placement:
          $ref: "#/components/schemas/LaunchPlacement"
        code:
          $ref: "#/components/schemas/LaunchCode"
        source:
          type: object
          additionalProperties: false
          required:
            - repo
            - ref
            - path
          description: For a launch, fetch this manifest file at the selected repository ref, then apply the other supplied manifest fields as overrides.
          properties:
            repo:
              type: string
              minLength: 1
            ref:
              type: string
              minLength: 1
            path:
              type: string
              minLength: 1
              pattern: ^(?!/)(?!.*(?:^|/)\.\.(?:/|$))[^\u0000]*$
        volume:
          $ref: "#/components/schemas/AttachVolume"
          description: BoxRouter-managed volume reference and access mode attached to the created sandbox.
    DockerfileBuild:
      type: object
      additionalProperties: false
      required:
        - dockerfile
      properties:
        dockerfile:
          type: string
          minLength: 1
          description: Dockerfile source sent to the provider's builder; never executed on the BoxRouter host.
        context_artifact_id:
          type: string
          minLength: 1
          description: |
            Workspace-owned tar build context. Reject paths escaping the context,
            special device files and unsafe links; enforce compressed and expanded
            size limits before using it. Source file and directory builder helpers
            package their inputs into this context rather than sending host paths.
    LaunchEnvironment:
      type: object
      propertyNames:
        pattern: ^[A-Za-z_][A-Za-z0-9_]*$
      additionalProperties:
        type: string
        pattern: ^[^\u0000]*$
      description: Non-secret environment values applied to setup and the launch.
    SandboxProviderSpecResources:
      type: object
      additionalProperties: false
      properties:
        cpu_min:
          type: number
          exclusiveMinimum: 0
        memory_min_mib:
          type: integer
          minimum: 1
        disk_min_mib:
          type: integer
          minimum: 1
        extendable_lifetime:
          type: boolean
          const: true
          description: Needed before POST /sandboxes/{sandbox_id}/lifetime may be called.
        region:
          type: string
          minLength: 1
          description: |
            One value from the region registry: a provider's region (for example `provider:eu-west`),
            an area (EU) or a country (US). Never a list.
        kernel:
          oneOf:
            - enum:
                - shared
                - filtered
                - dedicated
            - type: array
              minItems: 1
              uniqueItems: true
              items:
                enum:
                  - shared
                  - filtered
                  - dedicated
        paused_retention_seconds:
          type: number
          exclusiveMinimum: 0
        arch:
          oneOf:
            - enum:
                - amd64
                - arm64
            - type: array
              minItems: 1
              uniqueItems: true
              items:
                enum:
                  - amd64
                  - arm64
        run_seconds:
          type: integer
          minimum: 1
        process_seconds:
          type: integer
          minimum: 1
      description: |
        Resource constraints, execution limits, and environment facts that must hold for this
        sandbox. The matcher compares each named value with declared resources and capabilities;
        fields left out impose no requirement.
    LaunchPlacement:
      type: object
      additionalProperties: false
      description: |
        Provider preferences for which routes to use and how to rank them, and the route pin that
        a create takes as `route`. A route's `request_overrides` in the provider listing name
        `route` at the top level of a create; a launch takes it here, under `placement`.
      properties:
        preferences:
          $ref: "#/components/schemas/ProviderPreferences"
        route:
          $ref: "#/components/schemas/CreationRouteFilter"
    ProviderPreferences:
      type: object
      additionalProperties: false
      description: |
        The one provider-preference contract shared by sandbox creation, launch placement and
        Actions policy settings. Every property is optional; omitted properties inherit.
        Precedence, narrowest last: workspace restrictions are the ceiling, then a repository
        default or named Actions policy, then a workflow, then request controls. Hard
        restrictions (provider allowlists, route, account, supply, region, privacy, origin and
        spend) intersect and can only narrow; request ignore unions with workspace exclusions.
        A request true never lifts a workspace allow_fallbacks false. When workspace
        prevent_overrides is on, containing order, sort or allow_fallbacks is refused with
        routing_overrides_forbidden even if the value matches the workspace; `only` still
        narrows as a set and cannot reorder workspace ranking. Otherwise soft preference values
        replace inherited values without widening authority. `order: []` and `ignore: []` are valid empty soft
        arrays that name no preference and no exclusion; inherited hard authority is unchanged.
        Each list must hold distinct values; null is never valid for any property.
        `order` prioritizes named providers in listed order; eligible providers omitted from
        it remain candidates afterward, ranked by the existing router order. When `only`
        is supplied, it remains the hard ordered allowlist and caps eligible providers; `order`
        may rearrange its members, while omitted members retain `only` order. `ignore`
        excludes providers even when named by `only` or `order`. `sort` ranks routes
        within a provider. `allow_fallbacks` defaults to true and controls whether
        creation may dispatch a later eligible route after a provider definitively refuses before
        creating a sandbox. This includes another account for the same provider. False stops after
        the first route dispatch. Eligibility and preparation may skip a route before dispatch.
        Provider, account, supply, payer and requirements constraints remain binding.
      properties:
        order:
          type: array
          uniqueItems: true
          description: |
            Softly prioritize these providers in the listed order. Eligible providers omitted
            from this list remain candidates afterward using the existing router order. When
            `only` is supplied, it remains the ordered hard allowlist and caps eligibility;
            this order rearranges its members and omitted members retain their `only` order.
          items:
            type: string
            minLength: 1
            maxLength: 128
            pattern: ^[^\u0000]*$
        only:
          $ref: "#/components/schemas/ProviderAllowlist"
        ignore:
          type: array
          uniqueItems: true
          description: Providers to exclude from routing, even if also named in `only` or `order`.
          items:
            type: string
            minLength: 1
            maxLength: 128
            pattern: ^[^\u0000]*$
        sort:
          type: string
          enum:
            - price
            - time
          x-boxrouter-choices:
            price:
              label: Price
              line: Lowest hourly price first.
              measure: price
              better: lower
            time:
              label: Time
              line: Shortest run time measured on reference workloads first.
              measure: time
              better: lower
          description: |
            How eligible routes are ranked. `price` picks the lowest hourly price. `time` picks the shortest run time, measured on reference workloads.
        allow_fallbacks:
          type: boolean
          default: true
          description: |
            Whether Cloud may dispatch a later eligible route after a provider definitively
            refuses before creating a sandbox; defaults to true and continues until a route
            creates the sandbox or no eligible route remains. If `only` is supplied, it
            limits eligible providers; `preferences.order` takes precedence for their order, with
            `preferences.sort` ranking routes within each provider.
            False stops after the first route dispatch, including when a later route uses another
            account for the same provider. Local eligibility or preparation failures may skip a
            route before dispatch. This does not expand the provider allowlist or relax route,
            account, supply, payer or requirements restrictions.
    ProviderAllowlist:
      type: array
      minItems: 1
      uniqueItems: true
      description: |
        Used as `preferences.only`, this is the hard allowlist of eligible providers. `preferences.order`
        takes precedence for their order; otherwise this list's order applies. `preferences.sort`
        ranks eligible routes within each provider. Route, account, supply,
        payer and requirements restrictions still constrain every route, and unlisted providers
        are never considered. A one-provider list prevents cross-provider fallback while allowing
        later eligible routes for that provider when `preferences.allow_fallbacks` is true.
        Setting it to false stops after the first route dispatch, including a later account for
        the same provider.
      items:
        type: string
        minLength: 1
        maxLength: 128
        pattern: ^[^\u0000]*$
    CreationRouteFilter:
      type: object
      additionalProperties: false
      description: |
        The remaining route filters. Each singular value is a hard pin: `id` and `account_id` name
        exactly one route or connected account and `supply` one funding authority. Fallback never
        moves a pin, a volume remains attached to its selected route, and these
        filters only narrow workspace restrictions.
      properties:
        id:
          type: string
          minLength: 1
          maxLength: 128
          pattern: ^[^\u0000]*$
        account_id:
          type: string
          minLength: 1
          maxLength: 128
          pattern: ^[^\u0000]*$
          description: Connected account ID. A provider disabled on this deployment returns 409 provider_not_enabled before routing.
        supply:
          type: string
          enum:
            - byok
            - managed
    LaunchCode:
      type: object
      additionalProperties: false
      required:
        - repo
        - ref
      description: Clone a GitHub repository at the requested ref before setup.
      properties:
        repo:
          type: string
          minLength: 1
          pattern: ^(?:https://github\.com/)?[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+(?:\.git)?$
        ref:
          type: string
          minLength: 1
        directory:
          type: string
          minLength: 1
          pattern: ^(?!/)(?!.*(?:^|/)\.\.(?:/|$))[^\u0000]*$
          description: Relative directory in the checked-out repository.
    AttachVolume:
      type: object
      additionalProperties: false
      required:
        - id
      properties:
        id:
          $ref: "#/components/schemas/VolumeId"
        path:
          type: string
          pattern: ^/[^\u0000]*$
          default: /workspace
          description: |
            Absolute folder the checkpoint is restored into, replacing what the image has there. Repeated
            separators and `.` path components are normalized; any `..` component is rejected. The path
            must not be `/`, under /proc, /sys, /dev, /etc, /usr, /bin, /sbin, /lib or /run, or equal to
            or an ancestor of the internal `/tmp/boxrouter-volume` scratch path.
        checkpoint:
          type: integer
          minimum: 1
          description: |
            Checkpoint to restore; omitted means the head. An empty volume restores nothing. A
            checkpoint saved from a branch sandbox is not the head and can be selected by number with
            write, branch or read access; read mounts exactly that checkpoint's files, never the head.
        access:
          type: string
          enum:
            - write
            - read
            - shared
            - branch
          default: write
          description: |
            The access mode requested for this sandbox. Omitted means write. Launch modes are write,
            read, shared, and branch. Checkpoints and branches remain capability-gated.
    VolumeId:
      type: string
      pattern: ^vol_[0-9a-f]{32}$
      description: BoxRouter-managed Volume ID. Provider-native volume requirements are not accepted.
    AgentNotFoundErrorResponse:
      allOf:
        - $ref: "#/components/schemas/ApiErrorResponse"
        - type: object
          properties:
            error:
              type: object
              required:
                - code
              properties:
                code:
                  type: string
                  const: agent_not_found
                details:
                  type: object
                  properties:
                    agent:
                      type: string
                      minLength: 1
    LaunchRequest:
      type: object
      unevaluatedProperties: false
      allOf:
        - $ref: "#/components/schemas/LaunchRequestFields"
    LaunchRequestFields:
      type: object
      description: Exactly one of a catalog agent name or an inline manifest is required.
      properties:
        privacy:
          $ref: "#/components/schemas/PrivacyPolicy"
          description: Privacy floors for the sandbox created by this launch. Cloud carries this policy unchanged into deferred sandbox materialization.
        agent:
          type: string
          minLength: 1
          maxLength: 63
          pattern: ^[a-z][a-z0-9-]{0,62}$
        manifest:
          $ref: "#/components/schemas/LaunchManifest"
        overrides:
          $ref: "#/components/schemas/LaunchOverrides"
        additional_secret_refs:
          $ref: "#/components/schemas/AdditionalSecretReferences"
        credential_delivery:
          $ref: "#/components/schemas/CredentialDeliveryPolicy"
          description: Whether compatible environment fallback is allowed or protected delivery is required.
        run:
          $ref: "#/components/schemas/LaunchRun"
          description: Optional client supplied batch metadata. Each launch remains independent; the server stores the values on its launch operation, labels the sandbox group and copy, and echoes this object in the successful Launch result. The server does not check group completeness.
        registry_credentials:
          type: object
          additionalProperties: false
          required:
            - username
            - password
          properties:
            username:
              type: string
              minLength: 1
              pattern: ^[^\u0000]*$
            password:
              type: string
              minLength: 1
              writeOnly: true
              pattern: ^[^\u0000]*$
          description: |
            Request-only login for a manifest whose image is {private_oci}; required with it and
            refused without it. Both strings are non-empty, at most 64 KiB UTF-8 bytes together.
            It is passed to the provider to set up a copy of the image and is never stored,
            logged, traced or returned. Not accepted by unattended launch runs.
        secret_values:
          type: object
          propertyNames:
            $ref: "#/components/schemas/SecretName"
          additionalProperties:
            type: string
            writeOnly: true
            maxLength: 16384
            pattern: ^[^\u0000]*$
          description: |
            Request-only secret values for names declared by the resolved manifest. Unknown
            names are invalid_request. The server enforces request size limits outside JSON
            Schema validation. Values are never stored, returned or logged.
      oneOf:
        - required:
            - agent
        - required:
            - manifest
    PrivacyPolicy:
      type: object
      additionalProperties: false
      description: |
        Optional privacy requirements for a sandbox request. Omitted values inherit the
        workspace and customer-key floors; omission never resets them. Supplied values may
        only tighten those floors. `data_collection: deny` requires both no model training
        and zero provider data retention; a route that retains logs does not satisfy deny,
        even when `zdr` is false. `zdr: true` independently requires zero retention and is
        not a training-policy predicate. Neither setting consents to BoxRouter capture or
        promises that an eligible provider or end-to-end zero-retention route exists.
        `boxrouter_content_retention` separately restricts BoxRouter sandbox-content
        handling. Transient wins over ordinary across workspace, key, request and immutable
        origin floors; an ordinary request cannot relax an inherited transient floor.
        Unsupported transient operations and unqualified routes are refused. Omission
        preserves inheritance and the legacy combined meaning of request `zdr: true`;
        explicitly choosing ordinary separates BoxRouter retention from provider ZDR.
      properties:
        data_collection:
          type: string
          enum:
            - allow
            - deny
          description: Require the route to allow or deny provider data collection.
        zdr:
          type: boolean
          description: Require zero provider data retention when true.
        boxrouter_content_retention:
          type: string
          enum:
            - ordinary
            - transient
          description: |
            Ordinary allows normal bounded service retention; transient requires the
            supported non-persistent sandbox-content profile. This is not provider
            qualification, permission to train, or consent to capture additional content.
    LaunchManifest:
      anyOf:
        - $ref: "#/components/schemas/AgentManifest"
        - $ref: "#/components/schemas/LaunchSourceManifest"
      description: A complete inline agent manifest or a source wrapper whose command can come from the referenced file.
    LaunchSourceManifest:
      type: object
      additionalProperties: false
      required:
        - name
        - source
      description: Request wrapper that loads the base agent manifest from source. command may be omitted to use the command in that file; supplied fields override the loaded manifest.
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 63
          pattern: ^[a-z][a-z0-9-]{0,62}$
          description: A lowercase identifier for the agent.
        description:
          type: string
          description: Optional text describing the agent.
        image:
          oneOf:
            - type: string
              minLength: 1
              description: OCI image reference.
            - $ref: "#/components/schemas/DockerfileBuild"
            - type: object
              additionalProperties: false
              required:
                - private_oci
              properties:
                private_oci:
                  type: string
                  minLength: 1
                  description: OCI image reference in a registry that needs a login; send registry_credentials with the launch request.
          description: |
            Omit to use the standard image. {private_oci} names an image whose registry needs a
            login, sent as `registry_credentials` on the launch request; it launches only on
            routes that use your own provider keys, and not as an unattended run.
        setup:
          type: array
          items:
            type: string
            pattern: ^[^\u0000]*$
          description: Shell commands run in order before files and background commands.
        command:
          type: string
          minLength: 1
          pattern: ^[^\u0000]*$
          description: Command to run in an attached terminal or as an unattended process after preparation succeeds.
        env:
          $ref: "#/components/schemas/LaunchEnvironment"
          description: Non-secret environment values applied to setup and the launch.
        secrets:
          type: array
          uniqueItems: true
          items:
            $ref: "#/components/schemas/SecretName"
          description: Names whose values are resolved by the server and added only to the create environment.
        required_env:
          $ref: "#/components/schemas/AgentRequiredEnvironment"
        max_copies:
          $ref: "#/components/schemas/AgentMaximumCopies"
        model_access:
          type: array
          items:
            $ref: "#/components/schemas/ModelAccess"
          description: Named secrets that configure this catalog agent for model access. A launch may select at most one.
        sign_in:
          type: string
          minLength: 1
          maxLength: 256
          description: What to do inside the agent when no recognized model secret is selected.
        files:
          type: object
          propertyNames:
            pattern: ^(?:~/)?(?!/)(?!~/?$)(?!.*(?:^|/)\.\.(?:/|$))(?!.*(?:^|/)\.(?:/|$))(?!.*\/$)[^\u0000]+$
          additionalProperties:
            oneOf:
              - type: string
              - type: object
          description: Relative sandbox paths mapped to UTF-8 text or JSON objects, written before background commands.
        background:
          type: array
          items:
            type: string
            pattern: ^[^\u0000]*$
          description: Shell commands started detached during preparation before the launch command starts.
        ports:
          type: array
          uniqueItems: true
          items:
            type: integer
            minimum: 1
            maximum: 65535
          description: Ports to expose as previews. Naming ports adds the previews requirement to routing.
        interactive:
          type: boolean
          default: true
          description: When true, a client can attach a terminal; when false, launch starts the command unattended.
        preinstalled:
          type: boolean
          default: false
          description: Declares that the launch's image already carries this agent's harness. The catalog sets it for agents bundled in the agents image; the listAgents entry reports it.
        resources:
          $ref: "#/components/schemas/SandboxProviderSpecResources"
        lifetime_seconds:
          type:
            - integer
            - "null"
          minimum: 1
          description: "Requested sandbox lifetime in seconds for this launch; the sandbox may run past its lifetime deadline. Null asks for a sandbox that never ends. Only a provider that never ends a sandbox serves it, and only on the workspace's own provider account. Left out, the create's own default applies: rolling credit reservations on a credit-funded route that can extend a lifetime, else 300 seconds."
        placement:
          $ref: "#/components/schemas/LaunchPlacement"
        code:
          $ref: "#/components/schemas/LaunchCode"
        source:
          type: object
          additionalProperties: false
          required:
            - repo
            - ref
            - path
          description: For a launch, fetch this manifest file at the selected repository ref, then apply the other supplied manifest fields as overrides.
          properties:
            repo:
              type: string
              minLength: 1
            ref:
              type: string
              minLength: 1
            path:
              type: string
              minLength: 1
              pattern: ^(?!/)(?!.*(?:^|/)\.\.(?:/|$))[^\u0000]*$
        volume:
          $ref: "#/components/schemas/AttachVolume"
          description: BoxRouter-managed volume reference and access mode attached to the created sandbox.
    LaunchOverrides:
      type: object
      additionalProperties: false
      properties:
        image:
          oneOf:
            - type: string
              minLength: 1
              description: OCI image reference.
            - $ref: "#/components/schemas/DockerfileBuild"
        resources:
          $ref: "#/components/schemas/SandboxProviderSpecResources"
        lifetime_seconds:
          type:
            - integer
            - "null"
          minimum: 1
          description: "Requested sandbox lifetime in seconds, replacing the manifest value. The sandbox may run past its lifetime deadline. Null asks for a sandbox with no lifetime: it runs until destroyed, and only a provider that never ends a sandbox serves it, on the workspace's own provider account. If a chosen number exceeds every otherwise matching account's cap, launch fails with `lifetime_cap_exceeded`; the requested value is not lowered."
        code:
          $ref: "#/components/schemas/LaunchCode"
          description: Repository/ref to check out, overriding the manifest's code source.
        placement:
          $ref: "#/components/schemas/LaunchPlacement"
        env:
          $ref: "#/components/schemas/LaunchEnvironment"
    AdditionalSecretReferences:
      type: object
      propertyNames:
        pattern: ^[A-Za-z_][A-Za-z0-9_]*$
      additionalProperties:
        $ref: "#/components/schemas/SecretReference"
    SecretReference:
      type: object
      additionalProperties: false
      required:
        - secret_name
      properties:
        secret_name:
          $ref: "#/components/schemas/SecretName"
    CredentialDeliveryPolicy:
      type: string
      enum:
        - prefer_native
        - require_native
      default: prefer_native
    LaunchRun:
      type: object
      additionalProperties: false
      required:
        - id
        - index
        - count
      description: Client supplied identity and position for one independently managed launch.
      properties:
        id:
          type: string
          minLength: 22
          maxLength: 128
          description: Random group identifier chosen by the client with at least 128 bits of entropy.
        index:
          type: integer
          minimum: 0
          description: Zero-based position of this launch; must be less than count.
        count:
          type: integer
          minimum: 1
          description: Number of launches the client intends to submit; completeness is not checked.
    LaunchOperation:
      type: object
      additionalProperties: false
      required:
        - id
        - agent
        - repo
        - sha
        - status
        - current_phase
        - phases
        - result
        - error
        - failure_status
        - created_at
        - updated_at
      description: |
        Durable, workspace-owned launch work. Poll while queued or running. A succeeded
        operation contains its final Launch in result; a failed operation contains the
        original structured error, with the HTTP status recorded in failure_status.
        Secret values are never stored or returned.
      allOf:
        - if:
            properties:
              status:
                const: succeeded
            required:
              - status
          then:
            properties:
              result:
                $ref: "#/components/schemas/Launch"
              error:
                type: "null"
              failure_status:
                type: "null"
        - if:
            properties:
              status:
                const: failed
            required:
              - status
          then:
            properties:
              result:
                type: "null"
              error:
                $ref: "#/components/schemas/LaunchOperationError"
              failure_status:
                type: integer
                minimum: 400
                maximum: 599
      properties:
        id:
          type: string
          minLength: 1
        agent:
          type: string
          minLength: 1
        repo:
          type:
            - string
            - "null"
          minLength: 1
          description: Canonical resolved code repository, or null when the launch has no code checkout.
        sha:
          type:
            - string
            - "null"
          pattern: ^[0-9a-f]{40}$
          description: Immutable commit selected for the code checkout, or null when no code is requested.
        status:
          type: string
          enum:
            - queued
            - running
            - succeeded
            - failed
        current_phase:
          type:
            - string
            - "null"
          enum:
            - preparing_image
            - create
            - code
            - setup
            - files
            - background
            - previews
            - null
        phases:
          type: array
          items:
            $ref: "#/components/schemas/LaunchOperationPhase"
        result:
          oneOf:
            - $ref: "#/components/schemas/Launch"
            - type: "null"
        error:
          oneOf:
            - $ref: "#/components/schemas/LaunchOperationError"
            - type: "null"
        failure_status:
          type:
            - integer
            - "null"
          minimum: 400
          maximum: 599
        sandbox_id:
          type: string
          minLength: 1
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    Launch:
      type: object
      additionalProperties: false
      required:
        - id
        - sandbox_id
        - agent
        - phases
        - previews
        - expires_at
        - effective_lifetime_seconds
        - reused
      description: |
        Result of one launch. id and sandbox_id are the same BoxRouter-owned sandbox ID.
        The stored result of a launch. A matching idempotent retry returns this result unchanged.
      properties:
        interactive:
          type: boolean
          description: Whether the resolved manifest uses a terminal, after source manifests and overrides. False means launch preparation started the unattended command. Older servers may omit this field.
        id:
          type: string
          minLength: 1
        sandbox_id:
          type: string
          minLength: 1
        agent:
          type: string
          minLength: 1
          description: Name of the resolved catalog or inline manifest.
        phases:
          type: array
          items:
            $ref: "#/components/schemas/LaunchPhase"
        previews:
          type: array
          items:
            $ref: "#/components/schemas/LaunchPreview"
        expires_at:
          type:
            - string
            - "null"
          format: date-time
          description: The sandbox's lifetime deadline; null when it was created with no lifetime.
        effective_lifetime_seconds:
          type:
            - integer
            - "null"
          minimum: 1
          description: "Sandbox lifetime selected for this successful launch, in seconds: the lifetime the launch asked for, or the one the route armed when the manifest left it out (a rolling credit reservation window or the create's 300-second default). Null when the sandbox was created with no lifetime."
        reused:
          type: boolean
          description: Value recorded with the original launch result; it does not indicate whether the current create request is a replay.
        credential_delivery:
          $ref: "#/components/schemas/CredentialDelivery"
          description: Safe per-destination delivery mode and hosts; values and credential IDs are never returned. Omitted when the request has no typed selections or saved secret references.
        run:
          $ref: "#/components/schemas/LaunchRun"
          description: The run metadata supplied with this launch, echoed unchanged when present.
    LaunchPhase:
      type: object
      additionalProperties: false
      required:
        - name
        - status
        - seconds
      properties:
        name:
          type: string
          enum:
            - source
            - preparing_image
            - create
            - code
            - setup
            - files
            - background
            - previews
        status:
          type: string
          enum:
            - completed
        seconds:
          type: number
          minimum: 0
        command:
          type: string
          description: |
            The command run in this phase, when the phase executes a command. For the
            source phase, this is the resolved manifest command the client should open
            in the interactive terminal.
        repo:
          type: string
          minLength: 1
          description: Source repository resolved for a source phase.
        sha:
          type: string
          pattern: ^[0-9a-f]{40}$
          description: Immutable source commit resolved for a source phase.
        exit_code:
          type: integer
          description: Command exit status, when the phase executes a command.
    LaunchPreview:
      type: object
      additionalProperties: false
      required:
        - port
        - url
      properties:
        port:
          type: integer
          minimum: 1
          maximum: 65535
        url:
          type: string
          format: uri
    CredentialDelivery:
      type: object
      minProperties: 1
      propertyNames:
        pattern: ^[A-Za-z_][A-Za-z0-9_]*$
      additionalProperties:
        $ref: "#/components/schemas/CredentialDeliveryEntry"
    CredentialDeliveryEntry:
      type: object
      additionalProperties: false
      required:
        - mode
        - hosts
      properties:
        mode:
          type: string
          enum:
            - provider_protected
            - environment
        hosts:
          type: array
          uniqueItems: true
          items:
            $ref: "#/components/schemas/SandboxProviderSpecHostname"
      allOf:
        - if:
            properties:
              mode:
                const: provider_protected
            required:
              - mode
          then:
            properties:
              hosts:
                minItems: 1
        - if:
            properties:
              mode:
                const: environment
            required:
              - mode
          then:
            properties:
              hosts:
                maxItems: 0
    SandboxProviderSpecHostname:
      type: string
      oneOf:
        - pattern: ^(?=.{1,253}(?![\s\S]))(?![0-9.]+(?![\s\S]))[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?(?:\.[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?)*(?![\s\S])
        - pattern: ^(?=.{3,255}(?![\s\S]))\*\.(?![0-9.]+(?![\s\S]))[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?(?:\.[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?)*(?![\s\S])
    LaunchOperationError:
      type: object
      required:
        - error
      description: |
        Structured error retained by asynchronous execution. It has no request_id when the
        failure occurs after the launch request has returned 202; clients can correlate the
        failure by the operation id instead.
      properties:
        error:
          type: object
          required:
            - code
            - message
            - outcome
          properties:
            code:
              type: string
            message:
              type: string
            request_id:
              type: string
              description: Present only when the failure originated in the initial HTTP request.
            fix:
              type: string
            details:
              type: object
              description: Structured error context; shape depends on code.
              properties:
                phase:
                  type: string
                  enum:
                    - code
                    - setup
                    - files
                    - background
                    - previews
                command:
                  type: string
                  description: Present with exit_code and stderr_tail when a command failed.
                exit_code:
                  type: integer
                  description: Present with command and stderr_tail when a command failed.
                stderr_tail:
                  type: string
                  description: At most the final 4 KiB of stderr when a command failed; resolved secret values are redacted.
              dependentRequired:
                command:
                  - exit_code
                  - stderr_tail
                exit_code:
                  - command
                  - stderr_tail
                stderr_tail:
                  - command
                  - exit_code
            outcome:
              type: string
              enum:
                - not_started
                - completed
                - partial
                - unknown
    LaunchOperationPhase:
      type: object
      additionalProperties: false
      required:
        - name
        - status
        - seconds
      properties:
        name:
          type: string
          enum:
            - preparing_image
            - create
            - code
            - setup
            - files
            - background
            - previews
        status:
          type: string
          enum:
            - running
            - completed
            - failed
        seconds:
          type:
            - number
            - "null"
          minimum: 0
          description: Elapsed phase time in seconds; null while the phase is running.
        command:
          type: string
          description: The command run in this phase, when the phase executes a command.
        exit_code:
          type: integer
          description: Command exit status, when the phase executes a command.
    LaunchInputErrorResponse:
      oneOf:
        - $ref: "#/components/schemas/InvalidRequestErrorResponse"
        - $ref: "#/components/schemas/SecretMissingErrorResponse"
    InvalidRequestErrorResponse:
      allOf:
        - $ref: "#/components/schemas/ApiErrorResponse"
        - type: object
          properties:
            error:
              type: object
              required:
                - code
              properties:
                code:
                  type: string
                  const: invalid_request
    SecretMissingErrorResponse:
      allOf:
        - $ref: "#/components/schemas/ApiErrorResponse"
        - type: object
          properties:
            error:
              type: object
              required:
                - code
                - details
              properties:
                code:
                  type: string
                  const: secret_missing
                details:
                  type: object
                  required:
                    - names
                  properties:
                    names:
                      type: array
                      minItems: 1
                      uniqueItems: true
                      items:
                        $ref: "#/components/schemas/SecretName"
    LaunchConflictErrorResponse:
      oneOf:
        - $ref: "#/components/schemas/NoEligibleRouteErrorResponse"
        - $ref: "#/components/schemas/IdempotencyConflictErrorResponse"
        - $ref: "#/components/schemas/CopiesUnavailableErrorResponse"
    NoEligibleRouteErrorResponse:
      allOf:
        - $ref: "#/components/schemas/ApiErrorResponse"
        - type: object
          properties:
            error:
              type: object
              required:
                - code
                - fix
                - details
              properties:
                code:
                  type: string
                  const: no_eligible_route
                fix:
                  type: string
                  minLength: 1
                  description: |
                    The smallest change that would fit. On a command to an existing sandbox whose
                    creating API key has been revoked, the sandbox keeps that key's privacy settings
                    for its whole life and no other key may run commands in it: the response is this
                    409 with a failed reason of creator_key_revoked and a fix that says to create a
                    new sandbox with a current key. Nothing was dispatched, and deleting the sandbox
                    still works with any key in the workspace.
                details:
                  type: object
                  required:
                    - excluded
                  properties:
                    excluded:
                      type: array
                      items:
                        $ref: "#/components/schemas/RouteExclusion"
                    nearest:
                      type: array
                      items:
                        $ref: "#/components/schemas/Relaxation"
    RouteExclusion:
      type: object
      required:
        - provider
        - reasons
        - exclusions
      description: One route that cannot serve the request, with SandboxProviderSpec reasons.
      properties:
        provider:
          type: string
        reasons:
          type: array
          uniqueItems: true
          items:
            type: string
          description: SandboxProviderSpec match codes such as property_mismatch, capability_unsupported, prerequisite_missing or capacity_unavailable.
        exclusions:
          type: array
          description: |
            Each failing check, as the matcher reports it. Known fields are capability (the
            family), profile, property, requested, offered, rule, reason, explanation, upstream
            and evidence (source identifiers); clients must preserve fields they do not know.
          items:
            type: object
            properties:
              capability:
                type: string
              profile:
                type: string
              property:
                type: string
              rule:
                type: string
              reason:
                type: string
              explanation:
                type: string
              evidence:
                type: array
                items:
                  type: string
    Relaxation:
      type: object
      required:
        - provider
        - distance
        - failing
      description: |
        A route that would become eligible if the failing requirements were relaxed, nearest
        first. A suggestion only; nothing is relaxed automatically.
      properties:
        provider:
          type: string
        distance:
          type: integer
          minimum: 1
          description: Number of distinct failing (family, property) pairs.
        failing:
          type: array
          items:
            type: object
            required:
              - offered_by
            properties:
              capability:
                type:
                  - string
                  - "null"
              property:
                type:
                  - string
                  - "null"
              offered_by:
                type: array
                items:
                  type: string
                description: Other considered providers that offer the requested value.
    IdempotencyConflictErrorResponse:
      allOf:
        - $ref: "#/components/schemas/ApiErrorResponse"
        - type: object
          properties:
            error:
              type: object
              required:
                - code
                - fix
              properties:
                code:
                  type: string
                  const: conflict
                fix:
                  type: string
                  minLength: 1
                  description: Use a new idempotency key for a request with different input.
                outcome:
                  type: string
                  const: not_started
    CopiesUnavailableErrorResponse:
      allOf:
        - $ref: "#/components/schemas/ApiErrorResponse"
        - type: object
          properties:
            error:
              type: object
              required:
                - code
                - fix
              properties:
                code:
                  type: string
                  const: capability_unavailable
                fix:
                  type: string
                  minLength: 1
                  description: This deployment does not run launch-run copies; run the agent without copies.
                outcome:
                  type: string
                  const: not_started
    LaunchSettings:
      type: object
      additionalProperties: false
      required:
        - launch_id
        - source
        - placement
        - secrets
        - added_secrets
      description: |
        What one launch asked for, recorded when it was created. Choices and names only: secret
        values are never recorded.
      properties:
        launch_id:
          type: string
          minLength: 1
        source:
          description: The catalog agent, or the caller's own repository or image.
          oneOf:
            - type: object
              additionalProperties: false
              required:
                - kind
                - agent
              properties:
                kind:
                  const: agent
                agent:
                  type: string
                  minLength: 1
            - type: object
              additionalProperties: false
              required:
                - kind
                - name
                - repository
                - ref
                - checkout
              properties:
                kind:
                  const: repository
                name:
                  type: string
                  minLength: 1
                repository:
                  type: string
                  minLength: 1
                ref:
                  type: string
                  minLength: 1
                path:
                  type: string
                  minLength: 1
                  description: Manifest path inside the repository, when the launch read one.
                checkout:
                  type: boolean
                  description: True when the repository was checked out and the command run in it, rather than read for a manifest.
                command:
                  type: string
                  minLength: 1
            - type: object
              additionalProperties: false
              required:
                - kind
                - name
                - image
              properties:
                kind:
                  const: image
                name:
                  type: string
                  minLength: 1
                image:
                  type: string
                  minLength: 1
                command:
                  type: string
                  minLength: 1
            - type: object
              additionalProperties: false
              required:
                - kind
                - name
              description: An inline manifest that names neither a repository nor an image (for example a Dockerfile build).
              properties:
                kind:
                  const: manifest
                name:
                  type: string
                  minLength: 1
        placement:
          type: object
          additionalProperties: false
          required:
            - mode
            - providers
          properties:
            mode:
              type: string
              enum:
                - picks
                - chosen
              description: picks when BoxRouter chose the provider; chosen when the caller named one or more.
            providers:
              type: array
              items:
                type: string
                minLength: 1
              description: The providers the caller allowed; empty when BoxRouter picked.
        secrets:
          type: array
          items:
            type: string
            minLength: 1
          description: Names of every secret the launch used, declared by the agent or added by the caller.
        added_secrets:
          type: array
          items:
            type: string
            minLength: 1
          description: Names the caller added to the agent's own (`--secret` on the command line).
    LaunchCommand:
      type: object
      additionalProperties: false
      required:
        - launch_id
        - sandbox_id
        - command_started_at
        - timeout_seconds
        - state
        - exit_code
      description: |
        The bounded state of a launch's unattended command. This handle excludes command text,
        environment variables, private process identity and all output. `unknown` is returned
        when the command outcome cannot be confirmed.
      properties:
        launch_id:
          type: string
          minLength: 1
        sandbox_id:
          type: string
          minLength: 1
        command_started_at:
          type: string
          format: date-time
        timeout_seconds:
          type: integer
          minimum: 1
          description: Fixed command runtime bound set when the command started.
        state:
          type: string
          enum:
            - running
            - exited
            - timed_out
            - unknown
          x-boxrouter-open-enum: true
          description: Known observations. Clients must preserve unrecognized values for forward compatibility.
        exit_code:
          type:
            - integer
            - "null"
          description: Confirmed exit status for exited or timed_out commands; null while running or when the outcome is unknown.
    LaunchRunList:
      type: object
      additionalProperties: false
      required:
        - available
        - runs
        - next_cursor
        - total_copies
        - live_copies
      description: "One newest-first page of run summaries. The totals cover every run in the authenticated workspace, not only this page. When `available` is false this deployment does not run copies: the page is empty, the totals are zero, and the other launch-run operations answer 409 capability_unavailable."
      properties:
        available:
          type: boolean
          description: False when this deployment does not run launch-run copies; true otherwise.
        runs:
          type: array
          maxItems: 50
          items:
            $ref: "#/components/schemas/LaunchRunSummary"
        next_cursor:
          type:
            - string
            - "null"
          minLength: 1
          maxLength: 1024
          description: Opaque cursor for the next page; null on the last page.
        total_copies:
          type: integer
          minimum: 0
          description: Copies requested across all of the workspace's runs.
        live_copies:
          type: integer
          minimum: 0
          description: Copies across all of the workspace's runs whose sandboxes have not been confirmed absent.
    LaunchRunSummary:
      type: object
      additionalProperties: false
      required:
        - id
        - agent_name
        - copies
        - desired_state
        - created_at
        - live_copies
        - providers
      description: Lightweight run row for listLaunchRuns; copy items and command data are not included.
      properties:
        id:
          type: string
          minLength: 1
        agent_name:
          type:
            - string
            - "null"
          minLength: 1
          description: Manifest name of the resolved catalog agent or inline manifest stored with the run; null when the stored run has none. No other launch request or manifest data is returned.
        copies:
          type: integer
          minimum: 1
        desired_state:
          type: string
          enum:
            - running
            - stopped
          x-boxrouter-open-enum: true
        created_at:
          type: string
          format: date-time
        live_copies:
          type: integer
          minimum: 0
          description: Copies of this run whose sandboxes have not been confirmed absent, including stopping copies.
        providers:
          type: array
          uniqueItems: true
          items:
            type: string
            minLength: 1
          description: Sorted distinct providers of the workspace-owned sandboxes attached to this run's copies; empty until a sandbox is attached.
    LaunchRunRequest:
      type: object
      unevaluatedProperties: false
      description: One unattended launch run. Uses the canonical launch request fields and requires copies; request-only credential values are refused by the server before reservation.
      allOf:
        - $ref: "#/components/schemas/LaunchRequestFields"
        - type: object
          required:
            - copies
          properties:
            copies:
              type: integer
              minimum: 1
              description: Number of independently managed copies to reserve atomically.
    LaunchRunDocument:
      type: object
      additionalProperties: false
      required:
        - id
        - agent_name
        - copies
        - desired_state
        - created_at
        - items
      description: Plain metadata-only run response. It contains neither command observations nor output fields; GET is a provider-free read unless include=command is explicitly selected.
      properties:
        id:
          type: string
          minLength: 1
        agent_name:
          type:
            - string
            - "null"
          minLength: 1
          description: Manifest name of the resolved catalog agent or inline manifest stored with the run; null when the stored run has none. No other launch request or manifest data is returned.
        copies:
          type: integer
          minimum: 1
        desired_state:
          type: string
          enum:
            - running
            - stopped
          x-boxrouter-open-enum: true
        created_at:
          type: string
          format: date-time
        items:
          type: array
          items:
            $ref: "#/components/schemas/LaunchRunItem"
    LaunchRunItem:
      type: object
      additionalProperties: false
      required:
        - index
        - status
        - sandbox_id
        - provider
        - error
        - exit_code
        - finished_at
      description: One ordered copy's public lifecycle metadata; output contents are never included.
      properties:
        index:
          type: integer
          minimum: 0
        status:
          type: string
          enum:
            - queued
            - preparing
            - started
            - start_failed
            - stopping
            - stopped
            - finished
            - unknown
        sandbox_id:
          type:
            - string
            - "null"
        provider:
          type:
            - string
            - "null"
          minLength: 1
          description: Provider of the workspace-owned sandbox attached to this copy; null until a sandbox is attached.
        error:
          oneOf:
            - $ref: "#/components/schemas/LaunchRunCopyError"
            - type: "null"
        exit_code:
          type:
            - integer
            - "null"
        finished_at:
          type:
            - string
            - "null"
          format: date-time
    LaunchRunCopyError:
      type: object
      additionalProperties: false
      required:
        - code
        - message
        - fix
      properties:
        code:
          type:
            - string
            - "null"
        message:
          type:
            - string
            - "null"
        fix:
          type:
            - string
            - "null"
    LaunchRunRequestErrorResponse:
      anyOf:
        - $ref: "#/components/schemas/InvalidRequestErrorResponse"
        - $ref: "#/components/schemas/SecretMissingErrorResponse"
        - $ref: "#/components/schemas/UnsupportedFeatureErrorResponse"
    UnsupportedFeatureErrorResponse:
      allOf:
        - $ref: "#/components/schemas/ApiErrorResponse"
        - type: object
          properties:
            error:
              type: object
              required:
                - code
                - details
              properties:
                code:
                  type: string
                  const: unsupported_feature
                details:
                  type: object
                  required:
                    - feature
                  properties:
                    feature:
                      type: string
    LaunchRunWithCommand:
      type: object
      additionalProperties: false
      required:
        - id
        - agent_name
        - copies
        - desired_state
        - created_at
        - items
      description: Run response selected by GET include=command. The command member contains only bounded state and exit code; command text and collected output are never returned.
      properties:
        id:
          type: string
          minLength: 1
        agent_name:
          type:
            - string
            - "null"
          minLength: 1
          description: Manifest name of the resolved catalog agent or inline manifest stored with the run; null when the stored run has none. No other launch request or manifest data is returned.
        copies:
          type: integer
          minimum: 1
        desired_state:
          type: string
          enum:
            - running
            - stopped
          x-boxrouter-open-enum: true
        created_at:
          type: string
          format: date-time
        items:
          type: array
          items:
            anyOf:
              - $ref: "#/components/schemas/LaunchRunItem"
              - $ref: "#/components/schemas/LaunchRunItemWithCommand"
    LaunchRunItemWithCommand:
      type: object
      additionalProperties: false
      required:
        - index
        - status
        - sandbox_id
        - provider
        - error
        - exit_code
        - finished_at
      description: Copy metadata plus an opt-in bounded command observation.
      properties:
        index:
          type: integer
          minimum: 0
        status:
          type: string
          enum:
            - queued
            - preparing
            - started
            - start_failed
            - stopping
            - stopped
            - finished
            - unknown
        sandbox_id:
          type:
            - string
            - "null"
        provider:
          type:
            - string
            - "null"
          minLength: 1
          description: Provider of the workspace-owned sandbox attached to this copy; null until a sandbox is attached.
        error:
          oneOf:
            - $ref: "#/components/schemas/LaunchRunCopyError"
            - type: "null"
        exit_code:
          type:
            - integer
            - "null"
        finished_at:
          type:
            - string
            - "null"
          format: date-time
        command:
          $ref: "#/components/schemas/LaunchRunCommandObservation"
    LaunchRunCommandObservation:
      type: object
      additionalProperties: false
      required:
        - status
        - exit_code
      properties:
        status:
          type: string
          enum:
            - running
            - exited
            - timed_out
            - unknown
          x-boxrouter-open-enum: true
          description: Bounded command observation only. The response never includes command text, output, provider handles, or credential values. Clients preserve an unrecognized observation state for forward compatibility.
        exit_code:
          type:
            - integer
            - "null"
    LaunchRunStopAccepted:
      type: object
      additionalProperties: false
      required:
        - id
        - desired_state
        - stopped_at
      properties:
        id:
          type: string
          minLength: 1
        desired_state:
          type: string
          const: stopped
        stopped_at:
          type: string
          format: date-time
    LaunchRunCopyDestroyAccepted:
      type: object
      additionalProperties: false
      required:
        - run_id
        - copy_index
        - status
      description: Acknowledges a durable cleanup request for one copy. `stopping` is not confirmation that the sandbox is gone; read getLaunchRun for the copy's current status.
      properties:
        run_id:
          type: string
          minLength: 1
        copy_index:
          type: integer
          minimum: 0
        status:
          type: string
          const: stopping
    RepositoryAccess:
      type: object
      additionalProperties: false
      required:
        - repo
        - readable
        - visibility
        - sha
        - files
        - recipe
      description: Whether the workspace can read one repository, and what a launch would do with it.
      properties:
        repo:
          type: string
          description: Repository as owner/name.
        readable:
          type: boolean
        visibility:
          type:
            - string
            - "null"
          enum:
            - public
            - private
            - null
          description: public, or private when read through the workspace's GitHub connection; null when unreadable.
        sha:
          type:
            - string
            - "null"
          description: Commit the checked ref points at; null when unreadable.
        files:
          type:
            - array
            - "null"
          items:
            type: string
          description: Top-level entries at sha, directories ending in a slash; null when unreadable or the listing failed.
        recipe:
          type:
            - object
            - "null"
          additionalProperties: false
          required:
            - kind
            - hint
          properties:
            kind:
              type: string
              enum:
                - manifest
                - dockerfile
                - node
                - python
                - plain
            hint:
              type: string
              description: One sentence saying which setup applies.
            command:
              type: string
              description: Start command when one is known.
        code:
          type: string
          description: Machine-readable refusal code; present only when readable is false.
        message:
          type: string
          description: Why the repository cannot be read; present only when readable is false.
        fix:
          type: string
          description: What to do about it; present only when readable is false.
    GitHubInstallationPage:
      type: object
      additionalProperties: false
      required:
        - data
        - next_cursor
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/GitHubInstallation"
        next_cursor:
          type:
            - string
            - "null"
          minLength: 1
          maxLength: 128
          pattern: ^[^\u0000]*$
    GitHubInstallation:
      type: object
      additionalProperties: false
      required:
        - id
        - account_login
        - account_type
        - repository_selection
        - repository_count
        - linked_at
        - state
      properties:
        id:
          type: string
          minLength: 1
          maxLength: 128
          pattern: ^[^\u0000]*$
        account_login:
          type: string
          minLength: 1
          maxLength: 255
        account_type:
          type: string
          enum:
            - user
            - organization
        repository_selection:
          type: string
          enum:
            - all
            - selected
            - unknown
        repository_count:
          type: integer
          minimum: 0
        linked_at:
          type: string
          format: date-time
        state:
          type: string
          enum:
            - active
            - unavailable
    IdempotencyKeyValue:
      type: string
      minLength: 1
      maxLength: 128
      description: Stable identifier for one logical mutation.
    GitHubRepositoryPage:
      type: object
      additionalProperties: false
      required:
        - data
        - next_cursor
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/GitHubRepository"
        next_cursor:
          type:
            - string
            - "null"
          minLength: 1
          maxLength: 128
          pattern: ^[^\u0000]*$
    GitHubRepository:
      type: object
      additionalProperties: false
      required:
        - id
        - installation_id
        - full_name
        - default_branch
        - administrator
        - connection_state
      properties:
        id:
          type: string
          minLength: 1
          maxLength: 128
          pattern: ^[^\u0000]*$
        installation_id:
          type: string
          minLength: 1
          maxLength: 128
          pattern: ^[^\u0000]*$
        full_name:
          type: string
          minLength: 1
          maxLength: 512
        default_branch:
          type:
            - string
            - "null"
          minLength: 1
          maxLength: 255
        administrator:
          type: boolean
          description: Whether current GitHub permissions prove repository-administrator authority.
        private:
          type: boolean
          description: Whether GitHub reports the repository as private.
        connection_state:
          type: string
          enum:
            - verified
            - permission_required
            - unavailable
    ActionsRepositoriesResponse:
      type: object
      additionalProperties: false
      required:
        - data
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/ActionsRepositorySettingsEntry"
    ActionsRepositorySettingsEntry:
      type: object
      additionalProperties: false
      required:
        - settings
        - etag
      properties:
        etag:
          type: string
          maxLength: 256
          description: The strong settings revision the single-repository GET carries in its ETag header; send it as If-Match for updates once the record exists.
        settings:
          $ref: "#/components/schemas/ActionsRepositorySettings"
    ActionsRepositorySettings:
      type: object
      additionalProperties: false
      required:
        - repository_id
        - exists
        - enabled
        - policy_id
        - state
        - default_label
        - revision
        - updated_at
      properties:
        resolved_profile:
          type: string
          enum:
            - linux-basic
            - full
          x-boxrouter-open-enum: true
          readOnly: true
          description: Effective software profile resolved by GET from the saved repository policy, or linux-basic when no policy is selected. With exists=false this is the default, not a saved configuration. This does not claim image or route readiness. May be absent on older servers and write or idempotent replay responses; GET is authoritative. It does not change the settings CAS token.
        repository_id:
          type: string
          minLength: 1
          maxLength: 128
          pattern: ^[^\u0000]*$
        exists:
          type: boolean
          description: False only when no settings record exists; omit If-Match for its first write. True means If-Match is required.
        enabled:
          type: boolean
        policy_id:
          type:
            - string
            - "null"
          minLength: 1
          maxLength: 128
          pattern: ^[^\u0000]*$
        state:
          type: string
          enum:
            - disabled
            - preparing
            - ready
            - attention_required
        reason_code:
          type:
            - string
            - "null"
          maxLength: 128
          pattern: ^[a-z][a-z0-9_.-]{0,127}$
          description: Optional machine-readable reason for a non-ready repository state.
        message:
          type:
            - string
            - "null"
          minLength: 1
          maxLength: 512
          description: Optional plain-language explanation of the current repository state.
        link_target:
          type:
            - string
            - "null"
          enum:
            - byok
            - credits
            - null
          description: Optional destination when the repository state can be fixed in another page.
        default_label:
          type: string
          minLength: 1
          maxLength: 128
          description: The repository's selected BoxRouter runner label.
        revision:
          type: integer
          minimum: 1
        updated_at:
          type: string
          format: date-time
    UpdateActionsRepository:
      type: object
      additionalProperties: false
      required:
        - enabled
      properties:
        enabled:
          type: boolean
        policy_id:
          type:
            - string
            - "null"
          minLength: 1
          maxLength: 128
          pattern: ^[^\u0000]*$
          description: Optional workspace-owned policy. Omission or null selects automatic safe defaults.
    RoutingSettings:
      type: object
      additionalProperties: false
      required:
        - mode
        - provider_order
        - excluded_providers
        - allow_fallbacks
        - prevent_overrides
        - provider_funding
        - section_etags
      properties:
        mode:
          $ref: "#/components/schemas/mode"
        provider_order:
          $ref: "#/components/schemas/provider_order"
        excluded_providers:
          $ref: "#/components/schemas/excluded_providers"
        allow_fallbacks:
          $ref: "#/components/schemas/allow_fallbacks"
        prevent_overrides:
          $ref: "#/components/schemas/prevent_overrides"
        provider_funding:
          type: object
          propertyNames:
            $ref: "#/components/schemas/ProviderSlug"
          additionalProperties:
            $ref: "#/components/schemas/ProviderFunding"
        section_etags:
          type: object
          additionalProperties: false
          required:
            - routing
            - provider_funding
          properties:
            routing:
              type: string
              minLength: 1
            provider_funding:
              type: object
              propertyNames:
                $ref: "#/components/schemas/ProviderSlug"
              additionalProperties:
                type: string
                minLength: 1
          description: Opaque independent Routing and provider validators, never a document-wide ETag.
    mode:
      type: string
      enum:
        - automatic
        - cheapest
        - fastest
        - custom
      x-boxrouter-choices:
        automatic:
          label: Auto
          line: A random draw weighted toward lower hourly price.
        cheapest:
          label: Cheapest
          line: Lowest hourly price first.
          measure: price
          better: lower
        fastest:
          label: Fastest
          line: Shortest run time measured on reference workloads first.
          measure: time
          better: lower
        custom:
          label: Custom
          line: Your exact provider order, top to bottom.
    provider_order:
      type: array
      uniqueItems: true
      description: Stored in every mode; newly supported providers append in deployment order and start enabled.
      items:
        $ref: "#/components/schemas/ProviderSlug"
    ProviderSlug:
      type: string
      minLength: 1
      maxLength: 64
      pattern: ^[a-z][a-z0-9-]*$
    excluded_providers:
      type: array
      uniqueItems: true
      description: Hard deny-list; connecting a key does not clear its provider's exclusion.
      items:
        $ref: "#/components/schemas/ProviderSlug"
    allow_fallbacks:
      type: boolean
      description: After a definite pre-create refusal, try only later frozen routes in the same Prioritized, Credits or Fallback group; never retry unknown outcomes or created jobs.
    prevent_overrides:
      type: boolean
      default: false
      description: Reject requests containing preferences.order, preferences.sort or preferences.allow_fallbacks even when identical to the default; hard request restrictions still narrow.
    ProviderFunding:
      type: object
      additionalProperties: false
      required:
        - funding_order
      description: |
        Account IDs must belong to this workspace and provider, and must be unique.
        Keys above Credits are Prioritized; keys below it are Fallback. An unavailable
        managed provider keeps the managed row, its position and stored credits value
        without becoming routable. Never-offered providers have no managed row or credits.
        A later managed offer appends an off row after keys. A new key inserts last in
        Prioritized even in Custom; deleting a key removes that row. Account mutations
        update only this provider section and its ETag atomically with the account.
      properties:
        funding_order:
          type: array
          items:
            $ref: "#/components/schemas/FundingEntry"
        credits:
          type: boolean
          description: False preserves the managed row and position but makes it unusable.
      allOf:
        - if:
            required:
              - credits
          then:
            properties:
              funding_order:
                contains:
                  $ref: "#/components/schemas/ManagedFunding"
                minContains: 1
                maxContains: 1
        - if:
            properties:
              funding_order:
                contains:
                  $ref: "#/components/schemas/ManagedFunding"
            required:
              - funding_order
          then:
            required:
              - credits
        - not:
            properties:
              funding_order:
                contains:
                  $ref: "#/components/schemas/ManagedFunding"
                minContains: 2
    FundingEntry:
      oneOf:
        - $ref: "#/components/schemas/FundingAccount"
        - $ref: "#/components/schemas/ManagedFunding"
    FundingAccount:
      type: object
      additionalProperties: false
      required:
        - account_id
      properties:
        account_id:
          type: string
          minLength: 1
          maxLength: 128
    ManagedFunding:
      type: object
      additionalProperties: false
      required:
        - supply
      properties:
        supply:
          type: string
          const: managed
    RoutingSettingsUpdate:
      oneOf:
        - $ref: "#/components/schemas/RoutingSectionUpdate"
        - $ref: "#/components/schemas/ProviderFundingSectionUpdate"
      discriminator:
        propertyName: section
        mapping:
          routing: "#/components/schemas/RoutingSectionUpdate"
          provider_funding: "#/components/schemas/ProviderFundingSectionUpdate"
    RoutingSectionUpdate:
      type: object
      additionalProperties: false
      required:
        - section
        - routing
      description: |
        Routing update. Fields omitted from `routing` keep their stored values; the merged
        section is validated as a complete Routing section before it is written.
      properties:
        section:
          type: string
          const: routing
        routing:
          $ref: "#/components/schemas/RoutingFieldsUpdate"
    RoutingFieldsUpdate:
      type: object
      additionalProperties: false
      minProperties: 1
      description: |
        Partial Routing section: send only the fields to change. Every omitted field keeps
        its stored value. The merged section is validated as a complete Routing section.
      $defs:
        complete:
          $ref: "#/components/schemas/RoutingFields"
      properties:
        mode:
          $ref: "#/components/schemas/mode"
        provider_order:
          $ref: "#/components/schemas/provider_order"
        excluded_providers:
          $ref: "#/components/schemas/excluded_providers"
        allow_fallbacks:
          $ref: "#/components/schemas/allow_fallbacks"
        prevent_overrides:
          $ref: "#/components/schemas/prevent_overrides"
    RoutingFields:
      type: object
      additionalProperties: false
      required:
        - mode
        - provider_order
        - excluded_providers
        - allow_fallbacks
        - prevent_overrides
      properties:
        mode:
          type: string
          enum:
            - automatic
            - cheapest
            - fastest
            - custom
          x-boxrouter-choices:
            automatic:
              label: Auto
              line: A random draw weighted toward lower hourly price.
            cheapest:
              label: Cheapest
              line: Lowest hourly price first.
              measure: price
              better: lower
            fastest:
              label: Fastest
              line: Shortest run time measured on reference workloads first.
              measure: time
              better: lower
            custom:
              label: Custom
              line: Your exact provider order, top to bottom.
        provider_order:
          type: array
          uniqueItems: true
          description: Stored in every mode; newly supported providers append in deployment order and start enabled.
          items:
            $ref: "#/components/schemas/ProviderSlug"
        excluded_providers:
          type: array
          uniqueItems: true
          description: Hard deny-list; connecting a key does not clear its provider's exclusion.
          items:
            $ref: "#/components/schemas/ProviderSlug"
        allow_fallbacks:
          type: boolean
          description: After a definite pre-create refusal, try only later frozen routes in the same Prioritized, Credits or Fallback group; never retry unknown outcomes or created jobs.
        prevent_overrides:
          type: boolean
          default: false
          description: Reject requests containing preferences.order, preferences.sort or preferences.allow_fallbacks even when identical to the default; hard request restrictions still narrow.
    ProviderFundingSectionUpdate:
      type: object
      additionalProperties: false
      required:
        - section
        - provider
        - funding
      description: |
        One provider's funding update. Fields omitted from `funding` keep their stored values;
        the merged section is validated as a complete provider funding section before it is written.
      properties:
        section:
          type: string
          const: provider_funding
        provider:
          $ref: "#/components/schemas/ProviderSlug"
        funding:
          $ref: "#/components/schemas/ProviderFundingUpdate"
    ProviderFundingUpdate:
      type: object
      additionalProperties: false
      minProperties: 1
      description: |
        Partial provider funding section: send only the fields to change. Every omitted
        field keeps its stored value. The merged section is validated as a complete
        provider funding section, including its Credits and account invariants.
      properties:
        funding_order:
          type: array
          items:
            $ref: "#/components/schemas/FundingEntry"
        credits:
          type: boolean
          description: False preserves the managed row and position but makes it unusable.
    RoutingPreview:
      type: object
      additionalProperties: false
      required:
        - learning
        - can_start
        - skipped
        - worst_case_usd
        - credits_cover_cpu
      properties:
        learning:
          type: string
          enum:
            - on
            - off
          x-boxrouter-open-enum: true
        can_start:
          type: boolean
        skipped:
          type: array
          items:
            $ref: "#/components/schemas/RoutingSkipped"
        worst_case_usd:
          type: array
          items:
            $ref: "#/components/schemas/RoutingSizeQuote"
          description: Per-Actions-size quoted holds; empty if no managed quote is available.
        credits_cover_cpu:
          type:
            - integer
            - "null"
          minimum: 0
          description: Largest quoted Actions CPU floor covered by balance; null when unavailable.
    RoutingSkipped:
      type: object
      additionalProperties: false
      required:
        - provider
        - reason
      oneOf:
        - required:
            - account_id
        - required:
            - supply
      properties:
        provider:
          $ref: "#/components/schemas/ProviderSlug"
        account_id:
          type: string
          minLength: 1
          maxLength: 128
        supply:
          type: string
          const: managed
        reason:
          type: string
          enum:
            - byok_key_invalid
            - credits_exhausted
            - credits_below_hold
            - managed_provider_disabled
            - spend_limit_exceeded
          x-boxrouter-open-enum: true
        action:
          type: string
          enum:
            - add_key
            - fix_key
            - use_credits
          x-boxrouter-open-enum: true
          description: Permitted recovery action, omitted when none is available.
    RoutingSizeQuote:
      type: object
      additionalProperties: false
      required:
        - size
        - worst_case_usd
      properties:
        size:
          type: string
          minLength: 1
          description: Published Actions runner size label.
        worst_case_usd:
          type: string
          pattern: ^(0|[1-9][0-9]*)(\.[0-9]+)?$
          description: Full fixed-lifetime hold (run plus pickup) in USD, not a workspace cap.
    ActionsReadinessResponse:
      type: object
      additionalProperties: false
      required:
        - readiness
      properties:
        readiness:
          $ref: "#/components/schemas/ActionsReadiness"
        runner_catalog:
          $ref: "#/components/schemas/ActionsRunnerCatalog"
          description: Optional for compatibility with older servers; consumers must not invent recommended labels when absent. Prefer the `getActionsRunnerCatalog` operation, which serves the same document cacheably; this copy is retained only for older clients.
    ActionsReadiness:
      type: object
      additionalProperties: false
      required:
        - runners_ready
      properties:
        profile:
          type: string
          enum:
            - linux-basic
            - full
          x-boxrouter-open-enum: true
          description: Software profile covered by this readiness evidence. When repository-scoped, this is the saved profile for that repository; otherwise it is the workspace's basic readiness view.
        runners_ready:
          type: boolean
        providers:
          type: array
          items:
            $ref: "#/components/schemas/ActionsReadinessProvider"
        runner_labels:
          type: array
          items:
            $ref: "#/components/schemas/ActionsRunnerLabel"
        reason_code:
          type:
            - string
            - "null"
          enum:
            - funding_none
            - funding_unavailable
            - github_app_suspended
            - repository_unavailable
            - null
          x-boxrouter-open-enum: true
        disk_min_mib:
          type: integer
          minimum: 1
          description: Configured minimum disk floor for the selected profile in MiB. This is not provider allocation or observed free disk.
        message:
          type:
            - string
            - "null"
          minLength: 1
          maxLength: 512
        link_target:
          type:
            - string
            - "null"
          enum:
            - byok
            - credits
            - null
    ActionsReadinessProvider:
      type: object
      additionalProperties: false
      required:
        - provider
        - provider_label
        - account_id
        - account_label
        - supply
        - ready
        - region
        - size
        - reason_code
        - message
        - link_target
      properties:
        profile:
          type: string
          enum:
            - linux-basic
            - full
          x-boxrouter-open-enum: true
          description: Software profile covered by this readiness evidence. When repository-scoped, this is the saved profile for that repository; otherwise it is the workspace's basic readiness view.
        provider:
          type: string
          minLength: 1
          maxLength: 64
          pattern: ^[a-z][a-z0-9-]*$
        provider_label:
          type: string
          minLength: 1
          maxLength: 128
        account_id:
          type:
            - string
            - "null"
          minLength: 1
          maxLength: 128
        account_label:
          type:
            - string
            - "null"
          minLength: 1
          maxLength: 128
        supply:
          type:
            - string
            - "null"
          enum:
            - byok
            - managed
            - null
        ready:
          type: boolean
        region:
          type:
            - string
            - "null"
          minLength: 1
          maxLength: 128
        size:
          oneOf:
            - $ref: "#/components/schemas/ActionsMachineSize"
            - type: "null"
        cpu_options:
          type: array
          uniqueItems: true
          items:
            type: integer
            minimum: 1
        vcpu_options:
          deprecated: true
          description: Temporary compatibility field. Use cpu_options.
          type: array
          uniqueItems: true
          items:
            type: integer
            minimum: 1
        unavailable_shapes:
          type: array
          description: Runner sizes this route cannot serve, each with its own cause, so a size missing from cpu_options explains itself. A size is listed when the provider's declaration refuses it, when its image failed terminally, or when its image check is still pending. Omitted by older servers; empty when every size is available.
          maxItems: 16
          items:
            $ref: "#/components/schemas/ActionsUnavailableShape"
        max_job_minutes:
          type:
            - integer
            - "null"
          minimum: 1
        reason_code:
          type:
            - string
            - "null"
          description: repository_policy means no route on this provider matches the repository policy; needs_image_verification means image verification is pending; local_preview_unverified means this local preview cannot verify images.
          enum:
            - provider_not_connected
            - provider_unsupported_for_actions
            - byok_key_invalid
            - credits_exhausted
            - provider_outage
            - provider_capacity
            - repository_policy
            - needs_image_verification
            - local_preview_unverified
            - null
          x-boxrouter-open-enum: true
        message:
          type:
            - string
            - "null"
          minLength: 1
          maxLength: 512
        link_target:
          type:
            - string
            - "null"
          enum:
            - byok
            - credits
            - null
    ActionsMachineSize:
      type: object
      additionalProperties: false
      required:
        - cpu
        - memory_mib
      properties:
        cpu:
          type: number
          exclusiveMinimum: 0
          description: Normalized virtual CPU count.
        memory_mib:
          type: integer
          minimum: 1
          description: Normalized memory in MiB.
    ActionsUnavailableShape:
      type: object
      additionalProperties: false
      required:
        - cpu
        - memory_mib
        - reason_code
        - message
      properties:
        cpu:
          type:
            - integer
            - "null"
          minimum: 1
          description: Requested vCPU floor; null for the memory-selected size.
        memory_mib:
          type: integer
          minimum: 1
        reason_code:
          type: string
          minLength: 1
          maxLength: 64
          description: resource_mismatch means the provider declares smaller sandboxes; needs_image_verification means the size is still being checked; any other stable code (resource_limit, image_copy_failed, image_preparation_failed, probe_failed) is the latest terminal image failure.
        message:
          type: string
          minLength: 1
          maxLength: 512
    ActionsRunnerLabel:
      type: object
      additionalProperties: false
      required:
        - label
        - providers
      properties:
        profile:
          type: string
          enum:
            - linux-basic
            - full
          x-boxrouter-open-enum: true
          description: Software profile covered by this runner label's readiness evidence.
        label:
          type: string
          minLength: 1
          maxLength: 128
        objective:
          type:
            - string
            - "null"
          enum:
            - any
            - null
          x-boxrouter-choices:
            any:
              label: Any Fit
              line: on any available provider with this size.
          description: Always `any`; runner labels no longer select an ordering objective. Routing preference comes from the workspace Routing setting.
        size:
          $ref: "#/components/schemas/ActionsRunnerLabelSize"
        providers:
          type: array
          uniqueItems: true
          items:
            type: string
            minLength: 1
            maxLength: 64
            pattern: ^[a-z][a-z0-9-]*$
        objective_ranked:
          type: boolean
        vcpu:
          deprecated: true
          description: Temporary compatibility field. Use size.cpu.
          type: number
          exclusiveMinimum: 0
        memory_gb:
          deprecated: true
          description: Temporary compatibility field. Use size.memory_mib.
          type: number
          exclusiveMinimum: 0
        os_label:
          deprecated: true
          description: Temporary compatibility field. Derive this from the runner label.
          type:
            - string
            - "null"
          minLength: 1
          maxLength: 128
      anyOf:
        - required:
            - size
        - required:
            - vcpu
            - memory_gb
    ActionsRunnerLabelSize:
      type: object
      additionalProperties: false
      required:
        - memory_mib
      properties:
        cpu:
          type: number
          exclusiveMinimum: 0
          description: Normalized virtual CPU count when the provider declares it. Omitted for memory-selected labels without a CPU floor.
        memory_mib:
          type: integer
          minimum: 1
          description: Normalized memory in MiB.
    ActionsRunnerCatalog:
      description: Domain-owned recommended labels and requested resource floors. This catalog does not assert live route availability or verified software. A label selects the machine size and image tier only; routing preference comes from the workspace Routing setting. Removed label spellings are not accepted.
      type: object
      additionalProperties: false
      required:
        - version
        - runners
        - profiles
        - profile_source
        - standard_labels
        - default_label
      properties:
        version:
          type: integer
          const: 1
        runners:
          type: array
          minItems: 1
          items:
            $ref: "#/components/schemas/ActionsRunnerCatalogEntry"
        profiles:
          type: array
          minItems: 1
          uniqueItems: true
          items:
            type: string
            minLength: 1
        profile_source:
          type: string
          const: runner_label
        standard_labels:
          type: array
          uniqueItems: true
          items:
            type: string
            minLength: 1
        default_label:
          type: string
          minLength: 1
          maxLength: 200
    ActionsRunnerCatalogEntry:
      type: object
      additionalProperties: false
      required:
        - label
        - shape
        - profile
      properties:
        label:
          type: string
          minLength: 1
          maxLength: 200
          pattern: ^[A-Za-z0-9][A-Za-z0-9._-]*$
        shape:
          $ref: "#/components/schemas/ActionsRunnerCatalogShape"
        profile:
          type: string
          enum:
            - linux-basic
            - full
          x-boxrouter-open-enum: true
          description: Image tier this label selects; `full` is admitted only where the workspace's allowed_profiles covers it.
    ActionsRunnerCatalogShape:
      type: object
      additionalProperties: false
      required:
        - name
        - cpu
        - memory_mib
        - architecture
        - operating_system
      properties:
        name:
          type: string
          minLength: 1
        cpu:
          type: integer
          minimum: 1
        memory_mib:
          type: integer
          minimum: 1
        architecture:
          type: string
          minLength: 1
        operating_system:
          type: string
          minLength: 1
    ActionsPolicyPage:
      type: object
      additionalProperties: false
      required:
        - data
        - next_cursor
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/ActionsPolicy"
        next_cursor:
          type:
            - string
            - "null"
          minLength: 1
          maxLength: 128
          pattern: ^[^\u0000]*$
    ActionsPolicy:
      type: object
      additionalProperties: false
      required:
        - id
        - name
        - settings
        - revision
        - updated_at
      properties:
        id:
          type: string
          minLength: 1
          maxLength: 128
          pattern: ^[^\u0000]*$
        name:
          type: string
          minLength: 1
          maxLength: 64
          pattern: ^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$
        settings:
          $ref: "#/components/schemas/ActionsPolicySettings"
        revision:
          type: integer
          minimum: 1
        updated_at:
          type: string
          format: date-time
    ActionsPolicySettings:
      type: object
      additionalProperties: false
      properties:
        profile:
          type: string
          enum:
            - linux-basic
            - full
          default: linux-basic
          description: Stored preference only; the runner label selects the image tier at admission. `full` is admitted only where its image and route are verified and the workspace's allowed_profiles covers it.
        preferences:
          $ref: "#/components/schemas/ProviderPreferences"
          description: Soft provider order, exclusions, sort and fallback permission, shared with sandbox creation. They narrow and never add supply, accounts or authority.
        allowed_providers:
          type: array
          uniqueItems: true
          minItems: 1
          items:
            type: string
            minLength: 1
            maxLength: 64
            pattern: ^[a-z][a-z0-9-]*$
          description: Optional allowlist that can only narrow workspace-approved routes.
        allowed_regions:
          type: array
          uniqueItems: true
          minItems: 1
          items:
            type: string
            minLength: 1
            maxLength: 128
            pattern: ^[^\u0000]*$
          description: Reserved until Actions can verify each placement region. New non-null values are refused; a previously saved region restriction keeps admission closed rather than selecting an unverified region.
        allowed_route_ids:
          type: array
          uniqueItems: true
          minItems: 1
          items:
            type: string
            minLength: 1
            maxLength: 128
            pattern: ^[^\u0000]*$
          description: Optional list of exact route IDs owned by this workspace. It can only narrow workspace-approved routes; the IDs are validated against current workspace routes at admission.
        max_runtime_seconds:
          type: integer
          minimum: 1
          maximum: 1800
          description: Maximum runtime after GitHub actually assigns a job; pickup allowance is separate.
        max_customer_charge_usd:
          type:
            - string
            - "null"
          pattern: ^(0|[1-9][0-9]*)(\.[0-9]+)?$
          description: Optional exact US-dollar cap; null removes this policy's cap but does not widen workspace guardrails.
    CreateActionsPolicy:
      type: object
      additionalProperties: false
      required:
        - name
        - settings
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 64
          pattern: ^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$
        settings:
          $ref: "#/components/schemas/ActionsPolicySettings"
    UpdateActionsPolicy:
      type: object
      additionalProperties: false
      required:
        - name
        - settings
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 64
          pattern: ^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$
        settings:
          $ref: "#/components/schemas/ActionsPolicySettings"
    ActionsPreviewRequest:
      type: object
      additionalProperties: false
      required:
        - repository_id
      properties:
        repository_id:
          type: string
          minLength: 1
          maxLength: 128
          pattern: ^[^\u0000]*$
        label:
          type: string
          minLength: 1
          maxLength: 128
          default: boxrouter-ubuntu-24.04
          description: One BoxRouter runner label; absent means the catalog's default Ubuntu 24.04 runner.
        policy_id:
          type:
            - string
            - "null"
          minLength: 1
          maxLength: 128
          pattern: ^[^\u0000]*$
    ActionsPreviewResult:
      type: object
      additionalProperties: false
      required:
        - repository_id
        - label
        - status
        - eligible_routes
        - exclusions
        - reason
      properties:
        repository_id:
          type: string
          minLength: 1
          maxLength: 128
          pattern: ^[^\u0000]*$
        label:
          type: string
          minLength: 1
          maxLength: 128
        status:
          type: string
          enum:
            - eligible
            - refused
            - unknown
        eligible_routes:
          type: array
          items:
            $ref: "#/components/schemas/ActionsPreviewRoute"
        exclusions:
          type: array
          items:
            $ref: "#/components/schemas/ActionsPreviewExclusion"
        reason:
          type:
            - string
            - "null"
          minLength: 1
          maxLength: 1024
    ActionsPreviewRoute:
      type: object
      additionalProperties: false
      required:
        - provider
        - region
      properties:
        provider:
          type: string
          minLength: 1
          maxLength: 64
          pattern: ^[a-z][a-z0-9-]*$
        region:
          type:
            - string
            - "null"
          minLength: 1
          maxLength: 128
    ActionsPreviewExclusion:
      type: object
      additionalProperties: false
      required:
        - provider
        - code
        - reason
      properties:
        provider:
          type:
            - string
            - "null"
          minLength: 1
          maxLength: 64
          pattern: ^[a-z][a-z0-9-]*$
        code:
          type: string
          minLength: 1
          maxLength: 128
          description: Stable refusal category; clients must preserve future values.
        reason:
          type: string
          minLength: 1
          maxLength: 1024
    ActionsRunFilterText:
      type: string
      minLength: 1
      maxLength: 513
      pattern: ^[^\u0000]*$
    ActionsRunStateFilter:
      description: A run state, or the result status of an ended run.
      type: string
      enum:
        - queued
        - preparing
        - starting
        - running
        - stopping
        - ended
        - attention_required
        - succeeded
        - failed
        - cancelled
        - skipped
        - neutral
    ActionsRunFilterValues:
      type: array
      minItems: 1
      maxItems: 50
      uniqueItems: true
      items:
        $ref: "#/components/schemas/ActionsRunFilterText"
    ActionsRunPage:
      type: object
      additionalProperties: false
      required:
        - data
        - next_cursor
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/ActionsRun"
        next_cursor:
          type:
            - string
            - "null"
          minLength: 1
          maxLength: 128
          pattern: ^[^\u0000]*$
        counts:
          $ref: "#/components/schemas/ActionsRunCounts"
          description: Workspace-wide tab counts, independent of the query's filters.
        total:
          type: integer
          minimum: 0
          description: Number of runs matching this query's filters, across all pages.
    ActionsRun:
      type: object
      additionalProperties: false
      required:
        - id
        - github_run_id
        - run_attempt
        - repository_id
        - workflow_name
        - state
        - result
        - created_at
        - updated_at
        - billing
      properties:
        id:
          type: string
          minLength: 1
          maxLength: 128
          pattern: ^[^\u0000]*$
        github_run_id:
          type: string
          minLength: 1
          maxLength: 128
          pattern: ^[^\u0000]*$
        run_attempt:
          type: integer
          minimum: 1
        repository_id:
          type: string
          minLength: 1
          maxLength: 128
          pattern: ^[^\u0000]*$
        repository:
          type: string
          minLength: 1
          maxLength: 255
          description: Durable GitHub repository full name when known.
        workflow_name:
          type:
            - string
            - "null"
          minLength: 1
          maxLength: 255
        workflow:
          type: string
          minLength: 1
          maxLength: 255
          description: GitHub workflow name when known.
        branch:
          type: string
          minLength: 1
          maxLength: 255
          description: Head branch when known.
        run_url:
          type: string
          format: uri
          maxLength: 2048
          description: GitHub workflow run URL when known.
        head_branch:
          type:
            - string
            - "null"
          minLength: 1
          maxLength: 255
        head_sha:
          type: string
          minLength: 1
          maxLength: 128
          pattern: ^[^\u0000]*$
        run_number:
          type: integer
          minimum: 1
        display_title:
          type: string
          minLength: 1
        actor:
          $ref: "#/components/schemas/ActionsGitHubActor"
          description: Original actor GitHub recorded for the workflow run. Absent when unknown.
        triggering_actor:
          $ref: "#/components/schemas/ActionsGitHubActor"
          description: Actor who triggered the current run attempt
          separately recorded by GitHub. Absent when unknown.: null
        state:
          $ref: "#/components/schemas/ActionsRunState"
        result:
          $ref: "#/components/schemas/ActionsWorkResult"
        failure:
          $ref: "#/components/schemas/ActionsFailure"
        waiting:
          $ref: "#/components/schemas/ActionsRunWaiting"
        sources:
          type: object
          additionalProperties: true
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        providers:
          type: array
          uniqueItems: true
          items:
            type: string
            minLength: 1
            maxLength: 64
            pattern: ^[a-z][a-z0-9-]*$
          description: Provider IDs that ran this run's jobs, distinct, in job order. Absent when no job has been placed.
        started_at:
          type: string
          format: date-time
          description: When the first job of this run started. Absent until one has.
        completed_at:
          type:
            - string
            - "null"
          format: date-time
          description: When the last job of this run ended. Null while the run is active; absent when the run ended and no end time was confirmed.
        billing:
          $ref: "#/components/schemas/ActionsBilling"
        controls:
          $ref: "#/components/schemas/ActionsRunControls"
          description: Optional caller-specific workflow controls for this exact attempt. Mutation routes recheck authorization and the live GitHub attempt.
        cost:
          oneOf:
            - $ref: "#/components/schemas/ActionsCost"
            - type: "null"
          description: Sum of job costs. Null when one or more job costs are unknown; pending with a null amount when at least one job has no amount yet. Never presents a partial sum as the run total.
    ActionsGitHubActor:
      type: object
      additionalProperties: false
      required:
        - login
        - profile_url
      properties:
        login:
          type: string
          minLength: 1
          maxLength: 255
          pattern: ^[^\u0000]*$
        profile_url:
          type:
            - string
            - "null"
          format: uri
          maxLength: 2048
          pattern: ^https://github\.com/(apps/)?[^/?#]+$
          description: The actor's GitHub profile, or https://github.com/apps/<slug> for a GitHub App bot.
    ActionsRunState:
      type: string
      enum:
        - queued
        - preparing
        - starting
        - running
        - stopping
        - ended
        - attention_required
    ActionsWorkResult:
      type:
        - string
        - "null"
      enum:
        - success
        - workload_failure
        - cancelled
        - timed_out
        - infrastructure_failure
        - skipped
        - neutral
        - unknown
        - null
    ActionsFailure:
      type: object
      additionalProperties: false
      required:
        - kind
        - reason_code
        - message
        - link_target
      properties:
        kind:
          type: string
          enum:
            - workflow
            - boxrouter
        reason_code:
          type:
            - string
            - "null"
          maxLength: 128
          pattern: ^[a-z][a-z0-9_.-]{0,127}$
        message:
          type:
            - string
            - "null"
          minLength: 1
          maxLength: 1024
        link_target:
          type:
            - string
            - "null"
          enum:
            - byok
            - credits
            - github
            - null
    ActionsRunWaiting:
      type: object
      additionalProperties: false
      description: The longest-waiting job of a run that has jobs waiting. Absent when none waits and from servers that predate it.
      required:
        - job_count
        - reason_code
        - since
      properties:
        job_count:
          type: integer
          minimum: 1
        reason_code:
          type: string
          minLength: 1
          maxLength: 64
          pattern: ^[a-z][a-z0-9_]{0,63}$
        since:
          type: string
          format: date-time
    ActionsBilling:
      type: object
      additionalProperties: false
      required:
        - provider_cost
        - customer_charge
      properties:
        provider_cost:
          description: Existing sandbox provider-cost projection; null means no cost evidence is available.
          oneOf:
            - $ref: "#/components/schemas/SandboxCostSummary"
            - $ref: "#/components/schemas/Cost"
            - type: "null"
        customer_charge:
          $ref: "#/components/schemas/ActionsCharge"
    SandboxCostSummary:
      type: object
      additionalProperties: false
      required:
        - label
        - total
      description: |
        The label and total of a sandbox's cost, as list rows carry them. Both are the full cost's:
        see the `cost` schema for what they mean. Actions billing adds the cost's `summary`, the one
        sentence that says what the total is or why there is none; list rows do not carry it.
      properties:
        label:
          $ref: "#/components/schemas/CostLabel"
        total:
          $ref: "#/components/schemas/CostTotal"
        summary:
          $ref: "#/components/schemas/summary"
      if:
        properties:
          label:
            const: unknown
        required:
          - label
      then:
        properties:
          total:
            type: "null"
      else:
        properties:
          total:
            type: object
    CostLabel:
      description: "reported: a provider amount verified at list price; calculated: provider usage priced at stored list rates; unknown: required provider evidence or a rate is missing."
      enum:
        - reported
        - calculated
        - unknown
    CostTotal:
      description: An exact USD amount, low equal to high, or null for unknown.
      anyOf:
        - $ref: "#/components/schemas/CostTotalRange"
        - type: "null"
    CostTotalRange:
      description: "Exact USD total: low equals high."
      type: object
      additionalProperties: false
      required:
        - low
        - high
      properties:
        low:
          $ref: "#/components/schemas/PricingDecimal"
        high:
          $ref: "#/components/schemas/PricingDecimal"
    PricingDecimal:
      description: A non-negative decimal number as a string, in plain notation without an exponent.
      type: string
      pattern: ^(0|[1-9][0-9]*)(\.[0-9]+)?$
    summary:
      description: The total as one sentence, or why there is none. The sentences of `lines[].text` add up to it.
      type: string
      minLength: 1
    Cost:
      title: Cost
      description: A provider-sourced sandbox cost. Unknown has a null total; known totals are exact decimal strings. Hourly rate-card quotes may be ranges.
      type: object
      additionalProperties: false
      required:
        - label
        - total
        - lines
        - held_back_by
        - exclusions
        - price_version
        - as_of
      properties:
        label:
          description: "reported: a provider amount verified at list price; calculated: provider usage priced at stored list rates; unknown: required provider evidence or a rate is missing."
          enum:
            - reported
            - calculated
            - unknown
        total:
          description: An exact USD amount, low equal to high, or null for unknown.
          anyOf:
            - $ref: "#/components/schemas/CostTotalRange"
            - type: "null"
        lines:
          description: "The working: one line per charged resource. Empty when the provider's own figure is all we have, or when nothing could be worked out."
          type: array
          items:
            type: object
            additionalProperties: false
            required:
              - what
              - quantity
              - unit
              - rate
              - amount
              - quantity_source
            properties:
              what:
                enum:
                  - cpu
                  - memory
                  - disk
                  - gpu
                  - machine
                  - creation
                  - egress
                  - snapshot_storage
              quantity:
                $ref: "#/components/schemas/PricingDecimal"
              unit:
                description: The price version's unit for this rate, for example vcpu-second.
                type: string
                minLength: 1
              rate:
                description: US dollars per unit, as the price version states it.
                $ref: "#/components/schemas/PricingDecimal"
              amount:
                $ref: "#/components/schemas/PricingDecimal"
              quantity_source:
                description: The source of the usage facts the quantity came from; the least certain one when it came from several.
                $ref: "#/components/schemas/CostSource"
              text:
                description: "The line as a sentence a person reads, with its numbers: the quantity, the rate and the amount."
                type: string
                minLength: 1
              sources:
                description: "Where each number of the line came from, as words: the quantity, the rate and the amount."
                type: object
                additionalProperties: false
                required:
                  - amount
                properties:
                  quantity:
                    type: string
                    minLength: 1
                  rate:
                    type: string
                    minLength: 1
                  amount:
                    type: string
                    minLength: 1
        held_back_by:
          description: Reasons in plain words. Shared pricing findings are projected to their text for the HTTP response.
          type: array
          items:
            type: string
            minLength: 1
        exclusions:
          description: What the figure leaves out, such as plan fees, credits and discounts.
          type: array
          items:
            type: string
            minLength: 1
        price_version:
          description: The id of the price version used, which carries the quotes. Null when none was in effect.
          type:
            - string
            - "null"
          pattern: ^[a-z][a-z0-9-]*@[0-9]{4}-[0-9]{2}-[0-9]{2}(?:-([01][0-9]|2[0-3])[0-5][0-9][0-5][0-9]Z)?$
        as_of:
          description: When Cloud calculated or recorded the cost. A sandbox that has not ended has no cost yet.
          $ref: "#/components/schemas/PricingTimestamp"
        per_hour:
          description: US dollars per hour for an ended sandbox; no hourly cost is returned while it is running.
          $ref: "#/components/schemas/CostMoneyRange"
        reported_figure:
          description: Where a `reported` total came from. Present exactly when the label is `reported`.
          type: object
          additionalProperties: false
          required:
            - source
            - observed_at
            - how
          properties:
            source:
              $ref: "#/components/schemas/CostSource"
              const: provider
            observed_at:
              $ref: "#/components/schemas/PricingTimestamp"
            how:
              description: The API call that returned the figure.
              type: string
              minLength: 1
        summary:
          description: The total as one sentence, or why there is none. The sentences of `lines[].text` add up to it.
          type: string
          minLength: 1
      allOf:
        - if:
            properties:
              label:
                const: unknown
            required:
              - label
          then:
            properties:
              total:
                type: "null"
          else:
            properties:
              total:
                type: object
        - if:
            properties:
              label:
                const: reported
            required:
              - label
          then:
            required:
              - reported_figure
          else:
            not:
              required:
                - reported_figure
    CostSource:
      description: Where a fact came from. The same six values as the usage record's source; a test keeps the two lists equal.
      type: string
      enum:
        - provider
        - published
        - measured
        - recorded
        - calculated
        - unknown
    PricingTimestamp:
      description: A UTC time, ending in Z.
      type: string
      format: date-time
      pattern: ^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])T([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9](\.[0-9]+)?Z$
    CostMoneyRange:
      description: US dollars as a range; low equals high when the amount is exact.
      $ref: "#/components/schemas/CostDecimalRange"
    CostDecimalRange:
      type: object
      additionalProperties: false
      required:
        - low
        - high
      properties:
        low:
          $ref: "#/components/schemas/PricingDecimal"
        high:
          $ref: "#/components/schemas/PricingDecimal"
    ActionsCharge:
      type: object
      additionalProperties: false
      required:
        - state
        - held
        - final
      properties:
        state:
          type: string
          enum:
            - pending
            - final
            - unknown
        held:
          description: Credits held for this charge from the funding quote, if any; a hold, not an incurred provider cost or a final charge.
          oneOf:
            - $ref: "#/components/schemas/ActionsMoney"
            - type: "null"
        final:
          description: Final BoxRouter customer charge. Managed charges require settlement in the existing ledger; BYOK compute has an explicit zero charge because the connected provider bills the customer directly. Null while pending or unknown.
          oneOf:
            - $ref: "#/components/schemas/ActionsMoney"
            - type: "null"
        reason:
          type:
            - string
            - "null"
          x-boxrouter-open-enum: true
          enum:
            - byok_no_charge
            - held_not_final
            - evidence_unavailable
            - aggregate_unavailable
            - null
          description: Optional bounded explanation from the charge evidence already available to this read. It does not report settlement processing, retry or delay state.
      allOf:
        - if:
            properties:
              state:
                const: final
            required:
              - state
          then:
            properties:
              final:
                $ref: "#/components/schemas/ActionsMoney"
          else:
            properties:
              final:
                type: "null"
        - if:
            properties:
              state:
                const: unknown
            required:
              - state
          then:
            properties:
              held:
                type: "null"
              final:
                type: "null"
    ActionsMoney:
      type: object
      additionalProperties: false
      required:
        - currency
        - amount
      properties:
        currency:
          type: string
          const: usd
        amount:
          type: string
          minLength: 1
          pattern: ^(0|[1-9][0-9]*)(\.[0-9]+)?$
          description: Exact non-negative US-dollar amount in plain decimal notation.
    ActionsRunControls:
      type: object
      additionalProperties: false
      required:
        - cancel
        - rerun
      properties:
        cancel:
          $ref: "#/components/schemas/ActionsRunControl"
        rerun:
          $ref: "#/components/schemas/ActionsRunControl"
        rerun_failed_jobs:
          $ref: "#/components/schemas/ActionsRunControl"
          description: Whether the failed jobs of this attempt and jobs that depend on them may be rerun. Passed jobs may rerun when they depend on a failed job. Absent from servers that predate the control.
    ActionsRunControl:
      type: object
      additionalProperties: false
      required:
        - allowed
      properties:
        allowed:
          type: boolean
          description: Whether this caller may perform this control on the current run attempt.
        reason_code:
          type: string
          enum:
            - not_authorized
            - already_finished
            - not_current_attempt
            - not_finished
            - github_unavailable
            - no_failed_jobs
          description: Optional reason when allowed is false; omitted when the reason is not known.
    ActionsCost:
      type: object
      additionalProperties: false
      required:
        - amount
        - currency
        - status
        - basis
        - evidence
      properties:
        amount:
          description: Exact amount in currency, as a plain decimal string. Null while a cost is pending; unknown cost is represented by a null cost object, never by zero.
          oneOf:
            - type: string
              pattern: ^(0|[1-9][0-9]*)(\.[0-9]+)?$
            - type: "null"
        currency:
          type: string
          const: USD
        status:
          type: string
          enum:
            - final
            - pending
          description: Legacy provider cost. final requires the provider's own cost or usage records; pending has no amount. Estimated costs are unavailable and represented by a null cost object. Prefer billing.provider_cost.
        basis:
          type: string
          enum:
            - provider_reported
            - calculated
          description: Whether the amount came from provider-reported cost or from usage multiplied by a pinned price version.
        per_hour:
          type:
            - string
            - "null"
          pattern: ^(0|[1-9][0-9]*)(\.[0-9]+)?$
          description: Optional current USD-per-hour rate when all contributing sandboxes share one known rate; null when unavailable or mixed.
        evidence:
          type: array
          minItems: 1
          description: One source entry per distinct Actions sandbox included in the job or run total. Run evidence is the deduplicated union of its jobs' evidence.
          items:
            $ref: "#/components/schemas/ActionsCostEvidence"
      allOf:
        - if:
            properties:
              status:
                const: pending
            required:
              - status
          then:
            properties:
              amount:
                type: "null"
          else:
            properties:
              amount:
                type: string
                pattern: ^(0|[1-9][0-9]*)(\.[0-9]+)?$
        - if:
            properties:
              status:
                const: final
            required:
              - status
          then:
            properties:
              evidence:
                items:
                  properties:
                    ended_at:
                      type: string
                      format: date-time
                    usage_source:
                      const: provider
        - if:
            properties:
              basis:
                const: calculated
              amount:
                type: string
            required:
              - basis
              - amount
          then:
            properties:
              evidence:
                items:
                  properties:
                    price_version:
                      type: string
    ActionsCostEvidence:
      type: object
      additionalProperties: false
      required:
        - sandbox_id
        - usage_source
        - price_version
        - started_at
        - ended_at
      properties:
        sandbox_id:
          type: string
          minLength: 1
          maxLength: 128
          pattern: ^[^\u0000]*$
        usage_source:
          $ref: "#/components/schemas/CostSource"
        price_version:
          type:
            - string
            - "null"
          minLength: 1
          maxLength: 128
          description: Price version used for calculated cost, null when this evidence has no applicable price version.
        started_at:
          type: string
          format: date-time
          description: Start of this Actions sandbox's billable lifetime, from its durable lifetime record.
        ended_at:
          type:
            - string
            - "null"
          format: date-time
          description: Time destroy was confirmed; null while the Actions sandbox lifetime is still open or cleanup is unconfirmed.
    ActionsRunCounts:
      type: object
      additionalProperties: false
      required:
        - live
        - all
      properties:
        live:
          type: integer
          minimum: 0
          description: "Runs in progress: their state is queued, preparing, starting or running."
        all:
          type: integer
          minimum: 0
          description: All runs in the workspace.
    ActionsRunFacets:
      type: object
      additionalProperties: false
      required:
        - state
        - repository
        - workflow
        - triggered_by
        - branch
        - job
        - runner_label
      properties:
        state:
          $ref: "#/components/schemas/ActionsRunFacetList"
        repository:
          $ref: "#/components/schemas/ActionsRunFacetList"
          description: Values are repository IDs, for `repository_id`.
        workflow:
          $ref: "#/components/schemas/ActionsRunFacetList"
        triggered_by:
          $ref: "#/components/schemas/ActionsRunFacetList"
        branch:
          $ref: "#/components/schemas/ActionsRunFacetList"
        job:
          $ref: "#/components/schemas/ActionsRunFacetList"
          description: Values are unpadded base64url-encoded UTF-8 JSON `[workflow, job]`; labels are `Workflow › job`.
        runner_label:
          $ref: "#/components/schemas/ActionsRunFacetList"
    ActionsRunFacetList:
      type: array
      items:
        $ref: "#/components/schemas/ActionsRunFacetValue"
    ActionsRunFacetValue:
      type: object
      additionalProperties: false
      required:
        - value
        - label
        - count
      properties:
        value:
          type: string
          minLength: 1
          maxLength: 513
          description: What to send as the filter's parameter.
        label:
          type: string
          minLength: 1
          maxLength: 256
        count:
          type: integer
          minimum: 0
          description: Runs in the workspace that have this value.
    ActionsPoolIdleTimeReport:
      type: object
      additionalProperties: false
      required:
        - since
        - until
        - runner_count
        - idle_seconds
        - billing
        - pools
      properties:
        since:
          type: string
          format: date-time
          description: The start of the range read, inclusive.
        until:
          type: string
          format: date-time
          description: The end of the range read, exclusive.
        runner_count:
          type: integer
          minimum: 0
          description: Runners that started in the range and served no job.
        idle_seconds:
          type: integer
          minimum: 0
          description: Total lifetime of those runners.
        billing:
          description: Provider cost and customer charge of all those runners together; the charge is final only when every runner's charge is.
          $ref: "#/components/schemas/ActionsBilling"
        pools:
          type: array
          maxItems: 1000
          items:
            $ref: "#/components/schemas/ActionsPoolIdleTimePool"
    ActionsPoolIdleTimePool:
      type: object
      additionalProperties: false
      required:
        - pool_id
        - labels
        - runner_count
        - idle_seconds
        - billing
      properties:
        pool_id:
          type: string
          minLength: 1
          maxLength: 128
          pattern: ^[^\u0000]*$
          description: Opaque ID of the runner pool; it is the same for every runner with the same repository and labels.
        labels:
          type: array
          maxItems: 64
          items:
            type: string
            minLength: 1
            maxLength: 128
            pattern: ^[^\u0000]*$
          description: The runner labels that define the pool.
        runner_count:
          type: integer
          minimum: 0
          description: Runners of this pool that started in the range and served no job.
        idle_seconds:
          type: integer
          minimum: 0
        billing:
          $ref: "#/components/schemas/ActionsBilling"
    ActionsJobPage:
      type: object
      additionalProperties: false
      required:
        - data
        - next_cursor
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/ActionsJob"
        next_cursor:
          type:
            - string
            - "null"
          minLength: 1
          maxLength: 128
          pattern: ^[^\u0000]*$
    ActionsJob:
      type: object
      additionalProperties: false
      required:
        - id
        - run_id
        - github_job_id
        - name
        - state
        - result
        - placement_ids
      properties:
        id:
          type: string
          minLength: 1
          maxLength: 128
          pattern: ^[^\u0000]*$
        run_id:
          type: string
          minLength: 1
          maxLength: 128
          pattern: ^[^\u0000]*$
        github_job_id:
          type: string
          minLength: 1
          maxLength: 128
          pattern: ^[^\u0000]*$
        name:
          type: string
          minLength: 1
          maxLength: 255
        repository:
          type: string
          minLength: 1
          maxLength: 255
          description: Durable GitHub repository full name when known.
        workflow:
          type: string
          minLength: 1
          maxLength: 255
          description: GitHub workflow name when known.
        job_name:
          type: string
          minLength: 1
          maxLength: 255
          description: GitHub job name when known.
        job_url:
          type: string
          format: uri
          maxLength: 2048
          description: GitHub workflow job URL when known.
        run_url:
          type: string
          format: uri
          maxLength: 2048
          description: GitHub workflow run URL when known.
        branch:
          type: string
          minLength: 1
          maxLength: 255
          description: Head branch when known.
        head_sha:
          type: string
          minLength: 1
          maxLength: 128
          pattern: ^[^\u0000]*$
        requested_environment:
          $ref: "#/components/schemas/ActionsRequestedEnvironment"
        requested_labels:
          type:
            - array
            - "null"
          description: GitHub runner labels frozen from the verified workflow job request at admission; absent or null when historical evidence is unavailable.
          items:
            type: string
            minLength: 1
            maxLength: 128
            pattern: ^[^\u0000]*$
        state:
          $ref: "#/components/schemas/ActionsRunState"
        result:
          $ref: "#/components/schemas/ActionsWorkResult"
        failure:
          $ref: "#/components/schemas/ActionsFailure"
        waiting:
          $ref: "#/components/schemas/ActionsWaiting"
        machine:
          $ref: "#/components/schemas/ActionsMachine"
        billing:
          $ref: "#/components/schemas/ActionsBilling"
        cost:
          oneOf:
            - $ref: "#/components/schemas/ActionsCost"
            - type: "null"
          description: Cost for this job, summed across its distinct Actions sandboxes. Null when no trustworthy cost evidence is available.
        lifetime_seconds:
          type:
            - integer
            - "null"
          minimum: 1
        sources:
          type: object
          additionalProperties: true
          properties:
            region:
              $ref: "#/components/schemas/ActionsRegionSource"
        sandbox:
          oneOf:
            - $ref: "#/components/schemas/ActionsJobSandbox"
            - type: "null"
          description: The most recent durable placement sandbox for this job, including after cleanup.
        placement_ids:
          type: array
          uniqueItems: true
          items:
            type: string
            minLength: 1
            maxLength: 128
            pattern: ^[^\u0000]*$
        route_selection:
          $ref: "#/components/schemas/ActionsRouteSelection"
        creation:
          $ref: "#/components/schemas/CreationDiagnostics"
          description: Diagnostics of the latest placement's creation; each placement carries its own.
        ordered_routes:
          type: array
          description: Frozen route facts in the same order as route_selection.ordered_route_ids.
          items:
            $ref: "#/components/schemas/ActionsOrderedRoute"
    ActionsRequestedEnvironment:
      type: object
      additionalProperties: false
      description: Frozen admitted job request values only, from jobs.request_json metadata.actions.policy.profile and requirements. These are requested values, not configured or observed allocation. Null means the value was not recorded or is invalid; no defaults or current policy are inferred.
      required:
        - profile
        - cpu
        - memory_mib
        - disk_mib
      properties:
        profile:
          type:
            - string
            - "null"
          enum:
            - linux-basic
            - linux-docker
            - full
            - null
          x-boxrouter-open-enum: true
          description: Frozen at request time; jobs written before the label simplification may still read linux-docker.
        cpu:
          type:
            - integer
            - "null"
          minimum: 1
        memory_mib:
          type:
            - integer
            - "null"
          minimum: 1
        disk_mib:
          type:
            - integer
            - "null"
          minimum: 1
    ActionsWaiting:
      type: object
      additionalProperties: false
      description: Why a job that has not started is still waiting. A `running` job can also wait, while its registered runner waits for GitHub to hand it the job (reason_code `registered`); its run reads `starting` unless another of its jobs is further along. Present only while it waits; absent otherwise and from servers that predate it. `sources.waiting` names the record it came from.
      required:
        - reason_code
        - since
      properties:
        reason_code:
          type: string
          minLength: 1
          maxLength: 64
          pattern: ^[a-z][a-z0-9_]{0,63}$
          description: Bounded wait code, for example no_eligible_routes, insufficient_credit, compatible_slot_occupied, provider_capacity or admitted. Unknown codes are possible; clients show a generic waiting sentence.
        since:
          type: string
          format: date-time
          description: When the job began waiting for this reason.
        action:
          type:
            - string
            - "null"
          enum:
            - settings
            - credits
            - byok
            - github
            - null
          x-boxrouter-open-enum: true
          description: Where the customer can resolve the wait, when they can.
        required_nanodollars:
          type: string
          maxLength: 19
          pattern: ^(0|[1-9][0-9]{0,18})$
          description: What the job needs from the workspace's available credit, in billionths of a US dollar, the same figure the create refusal `insufficient_credit` reports. Present only for the reason_code `insufficient_credit`, and absent when Cloud did not record it.
        available_nanodollars:
          type: string
          maxLength: 19
          pattern: ^(0|[1-9][0-9]{0,18})$
          description: What the workspace had available when the job was refused, never below zero. Present only with `required_nanodollars`.
      dependentRequired:
        available_nanodollars:
          - required_nanodollars
    ActionsMachine:
      type: object
      additionalProperties: false
      description: What machine the job ran on and how fast it was against what is usual. Absent while the job has no recorded machine facts and from servers that predate it. `typical_score` and `speed_ratio` are null unless enough signed samples from enough workspaces stand behind the typical score.
      required:
        - integrity
      properties:
        cpu_model:
          type:
            - string
            - "null"
          maxLength: 96
          description: The CPU model the guest reported, as the provider shows it.
        effective_cpus:
          type:
            - integer
            - "null"
          minimum: 1
          description: CPUs the job could use.
        score:
          type:
            - integer
            - "null"
          minimum: 1
          description: CPU score of this machine, per CPU.
        integrity:
          type: string
          enum:
            - signed
            - unsigned
          x-boxrouter-open-enum: true
          description: signed when the guest proved the record with the job capability; only signed records are compared.
        steal_ratio:
          type:
            - number
            - "null"
          minimum: 0
          maximum: 1
          description: Share of CPU time the provider host took away during the job, when the guest can tell.
        typical_score:
          type:
            - integer
            - "null"
          minimum: 1
          description: Typical 7-day score per CPU for this provider, size and label.
        speed_ratio:
          type:
            - number
            - "null"
          exclusiveMinimum: 0
          description: typical_score divided by score; above 1 means this machine was slower than usual.
        memory_mib:
          type:
            - integer
            - "null"
          minimum: 1
          description: "Memory the machine reported: the cgroup limit when present, else the virtual machine total. Omitted when the provider does not report a trustworthy value. `facts.memory.basis` names which."
        disk_write_mbps:
          type:
            - integer
            - "null"
          minimum: 0
          description: Disk write speed in MB/s, measured by BoxRouter. `facts.disk` says whether it was measured on this job or on another machine of the same route.
        disk_read_mbps:
          type:
            - integer
            - "null"
          minimum: 0
          description: Disk read speed in MB/s, measured by BoxRouter. `facts.disk` says whether it was measured on this job or on another machine of the same route.
        facts:
          $ref: "#/components/schemas/ActionsMachineFacts"
          description: Where each shown machine value came from, one entry per value present. Omitted from servers that predate it.
    ActionsMachineFacts:
      type: object
      additionalProperties: false
      description: One source per shown machine value. A key is omitted when its value is.
      properties:
        cpu:
          type: object
          additionalProperties: false
          required:
            - kind
          properties:
            kind:
              type: string
              enum:
                - job_start
                - job_after_steps
              x-boxrouter-open-enum: true
              description: "job_start: read before the job steps ran. job_after_steps: an unsigned record read after the steps could run."
        memory:
          type: object
          additionalProperties: false
          required:
            - kind
            - basis
          properties:
            kind:
              type: string
              enum:
                - job_start
                - job_after_steps
              x-boxrouter-open-enum: true
            basis:
              type: string
              enum:
                - cgroup_limit
                - vm_total
              x-boxrouter-open-enum: true
              description: "cgroup_limit: the memory limit the machine reported. vm_total: the total memory the machine reported."
        disk:
          type: object
          additionalProperties: false
          required:
            - kind
          properties:
            kind:
              type: string
              enum:
                - job_start
                - job_after_steps
                - image_check
              x-boxrouter-open-enum: true
              description: "image_check: measured on another machine of the same route when BoxRouter last checked the runner image; this job's machine was not measured."
            observed_at:
              type: string
              format: date-time
              description: When an image_check was measured. Present only for image_check.
    ActionsRegionSource:
      type: object
      additionalProperties: true
      description: Where the region value came from. Other keys name the record read; they stay open.
      properties:
        kind:
          type: string
          enum:
            - provider_reported
            - account_setting
            - route_setting
            - provider_docs
          x-boxrouter-open-enum: true
          description: "provider_reported: the provider API reported it for this sandbox. account_setting: set on the provider account. route_setting: set on the route. provider_docs: the provider documents it as the default."
        url:
          type: string
          format: uri
          maxLength: 2048
          description: The documentation page, for provider_docs.
        read_at:
          type: string
          format: date
          description: Day the page was read, for provider_docs.
    ActionsJobSandbox:
      type: object
      additionalProperties: false
      required:
        - id
        - state
        - removed_at
      properties:
        id:
          type: string
          minLength: 1
          maxLength: 128
          pattern: ^[^\u0000]*$
        state:
          type: string
          enum:
            - creating
            - running
            - paused
            - stopped
            - destroying
            - destroyed
            - error
            - unknown
          x-boxrouter-open-enum: true
        removed_at:
          type:
            - string
            - "null"
          format: date-time
          description: Time cleanup was confirmed; null until the sandbox's removal is confirmed.
    ActionsRouteSelection:
      type: object
      additionalProperties: false
      required:
        - objective
        - ordered_route_ids
        - selected_route_id
        - reason
        - basis
        - considered
        - list_price_usd_per_hour
        - ready_ms_p50
        - readiness_scope
        - fallback
      properties:
        objective:
          type: string
          enum:
            - default
            - fastest
            - cheapest
          x-boxrouter-choices:
            default:
              label: Workspace setting
              line: Uses the workspace routing setting.
            fastest:
              label: Fastest
              line: Shortest run time measured on reference workloads first.
              measure: time
              better: lower
            cheapest:
              label: Cheapest
              line: Lowest hourly price first.
              measure: price
              better: lower
        ordered_route_ids:
          type: array
          minItems: 1
          uniqueItems: true
          items:
            type: string
            minLength: 1
            maxLength: 128
            pattern: ^[^\u0000]*$
        selected_route_id:
          type: string
          minLength: 1
          maxLength: 128
          pattern: ^[^\u0000]*$
        reason:
          type: string
          minLength: 1
          maxLength: 256
        basis:
          type: string
          enum:
            - observed
            - static
        considered:
          type: integer
          minimum: 1
        list_price_usd_per_hour:
          type:
            - string
            - "null"
          minLength: 1
          pattern: ^(0|[1-9][0-9]*)(\.[0-9]+)?$
        ready_ms_p50:
          type:
            - integer
            - "null"
          minimum: 0
        readiness_scope:
          type:
            - string
            - "null"
          enum:
            - route
            - provider_region
            - provider
            - none
            - null
        fallback:
          type: boolean
          description: Ranking marker with its released meaning unchanged. It does not name provider attempts or fallback permission; see `fallbacks_allowed` and `stopped_reason`.
        routed_away:
          type: object
          additionalProperties: false
          required:
            - provider
            - size_cpu
            - signal
            - window_minutes
          description: Present only when health demotion changes the selected route in Automatic or Fastest workspace mode for a Run with the applicable non-price `default` or `fastest` objective. Absent for legacy runs, when routing is off or shadow-only, and for Cheapest or Custom objectives.
          properties:
            provider:
              type: string
              minLength: 1
              maxLength: 64
              pattern: ^[a-z][a-z0-9-]*$
              description: Public provider id of the route that would have ranked first without the health signal. Uses the same vocabulary and bound as ActionsOrderedRoute.provider.
            size_cpu:
              type: integer
              minimum: 1
              description: CPU count of the job shape that was routed away; shapes without a CPU count do not produce this object.
            signal:
              type: string
              enum:
                - slow_machines
                - slow_starts
              x-boxrouter-open-enum: true
              description: slow_machines maps to a 60-minute window; slow_starts maps to a 15-minute window.
            window_minutes:
              type: integer
              minimum: 1
              description: 60 for slow_machines or 15 for slow_starts.
          allOf:
            - if:
                properties:
                  signal:
                    const: slow_machines
              then:
                properties:
                  window_minutes:
                    const: 60
            - if:
                properties:
                  signal:
                    const: slow_starts
              then:
                properties:
                  window_minutes:
                    const: 15
        fallbacks_allowed:
          type: boolean
          description: The effective allow_fallbacks for creating this placement; absent before this field existed.
        stopped_reason:
          $ref: "#/components/schemas/CreationStoppedReason"
        funding_group:
          $ref: "#/components/schemas/FundingGroup"
          description: Frozen payer group; absent on older responses. Definite pre-create refusal retries only later frozen candidates in this same group.
        workspace_mode:
          type: string
          enum:
            - automatic
            - cheapest
            - fastest
            - custom
          x-boxrouter-open-enum: true
          x-boxrouter-choices:
            automatic:
              label: Auto
              line: A random draw weighted toward lower hourly price.
            cheapest:
              label: Cheapest
              line: Lowest hourly price first.
              measure: price
              better: lower
            fastest:
              label: Fastest
              line: Shortest run time measured on reference workloads first.
              measure: time
              better: lower
            custom:
              label: Custom
              line: Your exact provider order, top to bottom.
          description: Effective workspace mode at admission; absent on older responses.
    CreationStoppedReason:
      type:
        - string
        - "null"
      enum:
        - fallbacks_disabled
        - no_eligible_route
        - uncertain_outcome
        - authority_changed
        - deadline_reached
        - cancelled
        - null
      x-boxrouter-open-enum: true
      description: |
        Why creation stopped dispatching further routes: `fallbacks_disabled` (allow_fallbacks was
        false and the one permitted dispatch was refused), `no_eligible_route` (every remaining
        route was excluded), `uncertain_outcome` (a dispatch may have allocated; never retried, reconcile
        with the operation), `authority_changed` (account, funding or privacy authority no longer
        permits the next route), `deadline_reached` and `cancelled`. Null when a route created the
        sandbox. Clients must tolerate values they do not know.
    FundingGroup:
      type: string
      enum:
        - prioritized
        - credits
        - fallback
      x-boxrouter-open-enum: true
      description: The frozen payer group; no cross-group retry after a dispatch refusal at launch.
    CreationDiagnostics:
      type: object
      additionalProperties: false
      description: |
        Bounded public diagnostics of one creation: the effective fallback permission, why
        dispatching stopped, the ordered dispatched attempts (the successful one included) and,
        separately, the routes skipped before dispatch. All fields are optional and absent when
        unknown. Each list holds at most 64 entries and is always an ordered prefix of the full
        list. When entries were omitted, the matching `*_truncated` flag is true and the `*_total`
        count gives the full length, so omitted candidates are never read as not dispatched.
        The confirmed binding is reported separately on the sandbox or placement. Prior-refusal
        lists elsewhere keep their meaning.
      properties:
        fallbacks_allowed:
          type: boolean
          description: The effective allow_fallbacks after inheritance.
        stopped_reason:
          $ref: "#/components/schemas/CreationStoppedReason"
        attempts:
          type: array
          maxItems: 64
          items:
            $ref: "#/components/schemas/CreationDiagnosticAttempt"
        attempts_truncated:
          type: boolean
          description: True when attempts holds only a prefix of the dispatched attempts.
        attempts_total:
          type: integer
          minimum: 0
          description: Full number of dispatched attempts; required when attempts_truncated is true.
        preparation_skips:
          type: array
          maxItems: 64
          items:
            $ref: "#/components/schemas/CreationDiagnosticSkip"
        preparation_skips_truncated:
          type: boolean
          description: True when preparation_skips holds only a prefix.
        preparation_skips_total:
          type: integer
          minimum: 0
          description: Full number of skipped routes; required when preparation_skips_truncated is true.
      allOf:
        - if:
            properties:
              attempts_truncated:
                const: true
            required:
              - attempts_truncated
          then:
            required:
              - attempts_total
        - if:
            properties:
              preparation_skips_truncated:
                const: true
            required:
              - preparation_skips_truncated
          then:
            required:
              - preparation_skips_total
    CreationDiagnosticAttempt:
      type: object
      additionalProperties: false
      required:
        - provider
        - outcome
        - native_code
        - native_status
      description: |
        A dispatched attempt in CreationDiagnostics, with the same meaning as CreationAttempt and
        bounded scalars. The operation readback keeps its unbounded legacy items.
      properties:
        provider:
          type: string
          minLength: 1
          maxLength: 64
        outcome:
          type: string
          enum:
            - completed
            - not_started
            - unknown
            - partial
          x-boxrouter-open-enum: true
          description: Clients must tolerate values they do not know.
        native_code:
          type:
            - string
            - "null"
          maxLength: 128
        native_status:
          type:
            - integer
            - "null"
        route_id:
          type:
            - string
            - "null"
          maxLength: 128
        account_id:
          type:
            - string
            - "null"
          maxLength: 128
        supply:
          type:
            - string
            - "null"
          maxLength: 32
        cause:
          $ref: "#/components/schemas/CreationRouteCause"
      allOf:
        - if:
            properties:
              outcome:
                not:
                  const: not_started
            required:
              - outcome
          then:
            properties:
              native_code:
                type: "null"
              native_status:
                type: "null"
    CreationRouteCause:
      type: string
      minLength: 1
      maxLength: 64
      enum:
        - key_rejected
        - plan_unbuildable
        - privacy_denied
        - not_ready
        - not_dispatched
        - provider_create_failed
      x-boxrouter-open-enum: true
      description: |
        Why one route did not create the sandbox, in a bounded word: `key_rejected` (the provider
        refused the saved key), `plan_unbuildable` (the route matched but its create could not be
        built), `privacy_denied` (the workspace's privacy settings refused the route),
        `not_ready` (the route's provider session was not ready in time), `not_dispatched`
        (refused locally before any provider call) and `provider_create_failed` (the provider was
        asked and the create failed). Clients must tolerate values they do not know.
    CreationDiagnosticSkip:
      type: object
      additionalProperties: false
      required:
        - provider
        - reason_code
      description: A route skipped locally before any provider dispatch, with bounded scalars. It is not an attempt.
      properties:
        provider:
          type: string
          minLength: 1
          maxLength: 64
        route_id:
          type:
            - string
            - "null"
          maxLength: 128
        account_id:
          type:
            - string
            - "null"
          maxLength: 128
        reason_code:
          type: string
          minLength: 1
          maxLength: 128
        cause:
          $ref: "#/components/schemas/CreationRouteCause"
    ActionsOrderedRoute:
      type: object
      additionalProperties: false
      required:
        - route_id
        - provider
        - provider_label
        - region
      properties:
        route_id:
          type: string
          minLength: 1
          maxLength: 128
          pattern: ^[^\u0000]*$
        provider:
          type: string
          minLength: 1
          maxLength: 64
          pattern: ^[a-z][a-z0-9-]*$
        provider_label:
          type: string
          minLength: 1
          maxLength: 128
        region:
          type:
            - string
            - "null"
          minLength: 1
          maxLength: 128
    ActionsPlacementPage:
      type: object
      additionalProperties: false
      required:
        - data
        - next_cursor
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/ActionsPlacement"
        next_cursor:
          type:
            - string
            - "null"
          minLength: 1
          maxLength: 128
          pattern: ^[^\u0000]*$
    ActionsPlacement:
      type: object
      additionalProperties: false
      required:
        - id
        - run_id
        - job_id
        - requested_job_id
        - assigned_job_id
        - provider
        - sandbox_id
        - state
        - result
        - cleanup_state
        - started_at
        - ended_at
        - billing
        - created_at
        - updated_at
      properties:
        id:
          type: string
          minLength: 1
          maxLength: 128
          pattern: ^[^\u0000]*$
        run_id:
          type: string
          minLength: 1
          maxLength: 128
          pattern: ^[^\u0000]*$
        job_id:
          type: string
          minLength: 1
          maxLength: 128
          pattern: ^[^\u0000]*$
        requested_job_id:
          type:
            - string
            - "null"
          minLength: 1
          maxLength: 128
          pattern: ^[^\u0000]*$
        assigned_job_id:
          type:
            - string
            - "null"
          minLength: 1
          maxLength: 128
          pattern: ^[^\u0000]*$
        route_id:
          type:
            - string
            - "null"
          minLength: 1
          maxLength: 128
          pattern: ^[^\u0000]*$
          description: The route whose provider actually created this placement's sandbox. Explicit null means no actual allocation is bound yet (creation is unconfirmed); it is never an admission-selected placeholder. Bound placements carry the nonempty route ID unchanged.
        provider:
          type:
            - string
            - "null"
          minLength: 1
          maxLength: 64
          pattern: ^[a-z][a-z0-9-]*$
        sandbox_id:
          type:
            - string
            - "null"
          minLength: 1
          maxLength: 128
          pattern: ^[^\u0000]*$
        sandbox_link:
          type:
            - string
            - "null"
          minLength: 1
          maxLength: 2048
          pattern: ^/(?!/)[^\u0000]*$
          description: Optional same-origin Sandboxes page path.
        region:
          type:
            - string
            - "null"
          minLength: 1
          maxLength: 128
        size:
          oneOf:
            - $ref: "#/components/schemas/ActionsMachineSize"
            - type: "null"
        supply:
          type:
            - string
            - "null"
          enum:
            - byok
            - managed
            - null
        sources:
          type: object
          additionalProperties: true
          properties:
            region:
              $ref: "#/components/schemas/ActionsRegionSource"
        state:
          $ref: "#/components/schemas/ActionsRunState"
        result:
          $ref: "#/components/schemas/ActionsWorkResult"
        cleanup_state:
          $ref: "#/components/schemas/ActionsCleanupState"
        started_at:
          type:
            - string
            - "null"
          format: date-time
        ended_at:
          type:
            - string
            - "null"
          format: date-time
        billing:
          $ref: "#/components/schemas/ActionsBilling"
        creation:
          $ref: "#/components/schemas/CreationDiagnostics"
        lifetime_seconds:
          type:
            - integer
            - "null"
          minimum: 1
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    ActionsCleanupState:
      type: string
      enum:
        - pending
        - confirmed
        - unknown
    ActionsJobSteps:
      type: object
      additionalProperties: false
      required:
        - job_id
        - run_id
        - github_job_id
        - run_attempt
        - availability
        - steps
        - observed_at
        - source
      properties:
        job_id:
          type: string
          minLength: 1
          maxLength: 128
          pattern: ^[^\u0000]*$
        run_id:
          type: string
          minLength: 1
          maxLength: 128
          pattern: ^[^\u0000]*$
        github_job_id:
          type: string
          minLength: 1
          maxLength: 128
          pattern: ^[^\u0000]*$
        run_attempt:
          type: integer
          minimum: 1
        availability:
          type: string
          enum:
            - available
            - pending
            - unavailable
            - forbidden
            - removed
        steps:
          type: array
          maxItems: 1000
          items:
            $ref: "#/components/schemas/ActionsWorkflowStep"
        observed_at:
          type: string
          format: date-time
        source:
          type: string
          enum:
            - github_workflow_job
        reason_code:
          type: string
          minLength: 1
          maxLength: 64
          pattern: ^[a-z][a-z0-9_]{0,63}$
    ActionsWorkflowStep:
      type: object
      additionalProperties: false
      required:
        - position
        - number
        - name
        - status
        - conclusion
        - started_at
        - completed_at
      properties:
        position:
          type: integer
          minimum: 0
        number:
          type: integer
          minimum: 1
        name:
          type: string
          minLength: 1
          maxLength: 255
        status:
          type: string
          minLength: 1
          maxLength: 64
        conclusion:
          type:
            - string
            - "null"
          maxLength: 64
        started_at:
          type:
            - string
            - "null"
          format: date-time
        completed_at:
          type:
            - string
            - "null"
          format: date-time
    ActionsJobLogs:
      type: object
      additionalProperties: false
      required:
        - job_id
        - run_id
        - github_job_id
        - run_attempt
        - observed_at
        - source
        - availability
        - text
        - byte_count
        - partial
      properties:
        job_id:
          type: string
          minLength: 1
          maxLength: 128
          pattern: ^[^\u0000]*$
        run_id:
          type: string
          minLength: 1
          maxLength: 128
          pattern: ^[^\u0000]*$
        github_job_id:
          type: string
          minLength: 1
          maxLength: 128
          pattern: ^[^\u0000]*$
        run_attempt:
          type: integer
          minimum: 1
        observed_at:
          type: string
          format: date-time
        source:
          type: string
          enum:
            - github_job_log
            - github_step_log
        availability:
          type: string
          enum:
            - available
            - pending
            - unavailable
            - forbidden
            - removed
        text:
          type:
            - string
            - "null"
          maxLength: 1048576
        byte_count:
          type: integer
          minimum: 0
          maximum: 1048576
          description: Number of source log bytes fetched, before UTF-8 decoding. An incomplete trailing UTF-8 sequence is omitted from text; byte_count still includes those fetched bytes.
        partial:
          type: boolean
        step_position:
          type: integer
          minimum: 0
        reason_code:
          type: string
          minLength: 1
          maxLength: 64
          pattern: ^[a-z][a-z0-9_]{0,63}$
      allOf:
        - if:
            properties:
              availability:
                const: available
            required:
              - availability
          then:
            properties:
              text:
                type: string
    ActionsJobEventPage:
      type: object
      additionalProperties: false
      required:
        - events
        - next_cursor
      properties:
        events:
          type: array
          maxItems: 1000
          items:
            $ref: "#/components/schemas/ActionsJobStepEvent"
        next_cursor:
          type:
            - string
            - "null"
          minLength: 1
          maxLength: 128
          pattern: ^[^\u0000]*$
    ActionsJobStepEvent:
      type: object
      additionalProperties: false
      required:
        - event_id
        - ts
        - step
        - outcome
        - reason_code
        - duration_ms
        - delivery_id
        - workspace_id
        - installation_id
        - repository_id
        - run_id
        - run_attempt
        - job_id
        - demand_id
        - route_id
        - sandbox_id
        - mutation_id
        - request_id
        - trace_id
      properties:
        event_id:
          type: string
          format: uuid
        ts:
          type: string
          format: date-time
        step:
          type: string
          enum:
            - webhook_received
            - admission
            - registration
            - image_verification
            - controller_pass
            - sandbox_create
            - launch
            - assignment
            - result
            - cleanup
        outcome:
          type: string
          enum:
            - ok
            - refused
            - ignored
            - deferred
            - unknown
            - error
        reason_code:
          type:
            - string
            - "null"
          maxLength: 64
          pattern: ^[a-z][a-z0-9_]{0,63}$
        duration_ms:
          type:
            - integer
            - "null"
          minimum: 0
        delivery_id:
          type:
            - string
            - "null"
        request_id:
          type:
            - string
            - "null"
          minLength: 1
          maxLength: 128
          pattern: ^[^\u0000]*$
        trace_id:
          type:
            - string
            - "null"
          minLength: 32
          maxLength: 32
          pattern: ^[0-9a-f]{32}$
        workspace_id:
          type: string
          minLength: 1
        installation_id:
          type:
            - string
            - "null"
        repository_id:
          type:
            - string
            - "null"
        run_id:
          type:
            - string
            - "null"
        run_attempt:
          type:
            - integer
            - "null"
          minimum: 0
        job_id:
          type:
            - string
            - "null"
        demand_id:
          type:
            - string
            - "null"
        route_id:
          type:
            - string
            - "null"
        sandbox_id:
          type:
            - string
            - "null"
        mutation_id:
          type:
            - string
            - "null"
    ActionsRunControlResult:
      type: object
      additionalProperties: false
      required:
        - operation_id
        - run_id
        - action
        - scope
        - outcome
        - github_run_id
        - run_attempt
      properties:
        operation_id:
          type: string
          minLength: 1
          maxLength: 128
          pattern: ^[^\u0000]*$
        run_id:
          type: string
          minLength: 1
          maxLength: 128
          pattern: ^[^\u0000]*$
        action:
          type: string
          enum:
            - cancel
            - rerun
            - rerun_failed_jobs
        scope:
          type: string
          const: entire_workflow
        outcome:
          type: string
          enum:
            - accepted
            - unknown
        github_run_id:
          type:
            - string
            - "null"
          minLength: 1
          maxLength: 128
          pattern: ^[^\u0000]*$
        run_attempt:
          type:
            - integer
            - "null"
          minimum: 1
    Operation:
      type: object
      description: Durable record of a mutation's outcome and identifiers. Error detail is returned with the original response and correlated by request_id; it is not stored in this record. Creation operations may include a sanitized route and selection summary. These fields are absent for non-create operations. Attempts may name the public route, account and supply they dispatched; payer and child identifiers, provider messages and credentials are never exposed.
      required:
        - id
        - operation
        - outcome
        - observed_at
        - sandbox_id
      properties:
        id:
          type: string
        operation:
          type: string
          description: OpenAPI operation ID of the attempted mutation.
        outcome:
          type: string
          enum:
            - not_started
            - completed
            - partial
            - unknown
        observed_at:
          type: string
          format: date-time
        sandbox_id:
          type:
            - string
            - "null"
          description: Owned BoxRouter resource when established, including recovered creations.
        settled_by:
          type: string
          enum:
            - wait_timeout
            - lifetime_elapsed
          x-boxrouter-open-enum: true
          description: "Present when BoxRouter settled the operation itself rather than observing its end. `wait_timeout`: a command's wait ran out, so `outcome` is `completed` for BoxRouter's request but the command may still be running at the provider. `lifetime_elapsed`: a create whose result BoxRouter could never read, closed once the provider's maximum sandbox lifetime had passed; `outcome` stays `unknown` because BoxRouter cannot tell whether a sandbox was made, but any sandbox it made has ended, and its cost is unknown. Clients must tolerate values they do not know; absence means the outcome was observed."
        native_resource_id:
          type: string
          minLength: 1
          description: Recorded opaque handle for the operation's provider-specific resource, when known. Use it only with the original sandbox and the matching native operation family. This identifier does not grant access or prove completion. Credentials are never stored in this field; absence means no handle was recorded.
        route:
          type: object
          additionalProperties: false
          description: Sanitized selected route for a create operation. Omitted for non-create operations. Contains no payer or child identifiers.
          required:
            - provider
            - account_id
            - supply
            - selection
          properties:
            provider:
              type: string
              minLength: 1
            account_id:
              type:
                - string
                - "null"
            supply:
              type: string
              minLength: 1
            selection:
              type: object
              additionalProperties: false
              required:
                - ranked_by
                - attempts
                - preparation_skips
              properties:
                ranked_by:
                  type:
                    - string
                    - "null"
                attempts:
                  type: array
                  description: Provider dispatch attempts in order. Native refusal evidence is present only for a durable definite refusal; unknown outcomes never carry refusal evidence.
                  items:
                    $ref: "#/components/schemas/CreationAttempt"
                preparation_skips:
                  type: array
                  description: Routes skipped locally before a provider dispatch.
                  items:
                    $ref: "#/components/schemas/CreationPreparationSkip"
                fallbacks_allowed:
                  type: boolean
                  description: The effective allow_fallbacks after inheritance.
                stopped_reason:
                  $ref: "#/components/schemas/CreationStoppedReason"
      allOf:
        - if:
            properties:
              operation:
                not:
                  const: createSandbox
            required:
              - operation
          then:
            not:
              required:
                - route
    CreationAttempt:
      type: object
      additionalProperties: false
      required:
        - provider
        - outcome
        - native_code
        - native_status
      description: |
        One provider dispatch made while creating, in dispatch order, including the one that
        created the sandbox. `outcome` is the operation vocabulary: completed, not_started
        (a definite refusal before any allocation), unknown and partial. Native refusal
        evidence is present only for a durable definite refusal. The route, account and supply
        name what was dispatched, not a confirmed binding, which is reported separately on the
        sandbox or placement. Payer, child and native payload identifiers are never returned.
      properties:
        provider:
          type: string
          minLength: 1
        outcome:
          type: string
          enum:
            - completed
            - not_started
            - unknown
            - partial
        native_code:
          type:
            - string
            - "null"
        native_status:
          type:
            - integer
            - "null"
        route_id:
          type:
            - string
            - "null"
          maxLength: 128
          description: The route this dispatch used.
        account_id:
          type:
            - string
            - "null"
          maxLength: 128
          description: The connected account this dispatch used.
        supply:
          type:
            - string
            - "null"
          maxLength: 32
          description: "The funding authority of this dispatch: byok or managed."
        cause:
          $ref: "#/components/schemas/CreationRouteCause"
      allOf:
        - if:
            properties:
              outcome:
                enum:
                  - completed
                  - unknown
                  - partial
            required:
              - outcome
          then:
            properties:
              native_code:
                type: "null"
              native_status:
                type: "null"
    CreationPreparationSkip:
      type: object
      additionalProperties: false
      required:
        - provider
        - reason_code
      description: A route skipped locally before any provider dispatch. It is not an attempt.
      properties:
        provider:
          type: string
          minLength: 1
        route_id:
          type:
            - string
            - "null"
          maxLength: 128
          description: The route that was skipped.
        account_id:
          type:
            - string
            - "null"
          maxLength: 128
          description: The connected account of the skipped route.
        reason_code:
          type: string
          minLength: 1
        cause:
          $ref: "#/components/schemas/CreationRouteCause"
    Artifact:
      type: object
      required:
        - id
        - size_bytes
        - sha256
        - expires_at
      properties:
        id:
          type: string
        size_bytes:
          type: integer
          minimum: 0
        sha256:
          type: string
          pattern: ^[a-f0-9]{64}$
        expires_at:
          type: string
          format: date-time
          description: Staging retention deadline. This is not a persistent volume or checkpointed volume data.
    PrepareImage:
      type: object
      additionalProperties: false
      description: |
        Two forms; send exactly one. `{image, providers, sizes?}` prepares on the named
        providers. `{match, top?}` takes the body you send to POST /sandboxes or
        POST /sandboxes/match: Cloud ranks routes exactly as that create would now, under this
        workspace's payer rules, and prepares `match.image` on the first `top` distinct
        providers, at `match.resources`. Fields that do not choose a route, such as env and
        labels, are ignored. Ranking can change later, for example when prices or measured
        start times change, so a later create may pick a provider that was not prepared.
      properties:
        image:
          description: |
            Image to prepare. Only with `providers`. A string is an OCI image reference. Use
            {private_oci} for an image whose registry needs a login, together with
            `registry_credentials`; it is prepared only on routes that use your own provider keys.
            A Dockerfile object uses the same source text and optional uploaded context artifact
            as a sandbox create. Only offerings declaring that prepared source and context mode
            are selected. The whole JSON request is limited to 64 KiB.
          oneOf:
            - type: string
              minLength: 1
              not:
                const: provider_default
            - type: object
              additionalProperties: false
              required:
                - private_oci
              properties:
                private_oci:
                  type: string
                  minLength: 1
                  description: OCI image reference in a registry that needs a login.
            - type: object
              additionalProperties: false
              required:
                - dockerfile
              properties:
                dockerfile:
                  type: string
                  minLength: 1
                  pattern: ^[^\u0000]*$
                  description: Dockerfile source text. No inline context files or context URLs.
                context_artifact_id:
                  type: string
                  minLength: 1
                  description: |
                    Optional unexpired artifact owned by this workspace. Its tar context is
                    leased through background preparation. Existing upload limits apply:
                    10 MiB archive, 64 MiB expanded regular files and 2,048 entries.
        registry_credentials:
          type: object
          additionalProperties: false
          required:
            - username
            - password
          properties:
            username:
              type: string
              minLength: 1
              pattern: ^[^\u0000]*$
            password:
              type: string
              minLength: 1
              writeOnly: true
              pattern: ^[^\u0000]*$
          description: |
            Request-only login for a private_oci `image`; required with it and refused without it.
            With `match`, send it inside the match body instead. Both strings are non-empty, at
            most 64 KiB UTF-8 bytes together. It is passed to the provider to set up the copy and
            is never stored, logged, traced or returned.
        providers:
          $ref: "#/components/schemas/ImageProviders"
        sizes:
          type: array
          minItems: 1
          uniqueItems: true
          items:
            type: integer
            minimum: 1
          description: Optional memory sizes to prepare, expressed in GiB. Only with `providers`.
        match:
          allOf:
            - $ref: "#/components/schemas/MatchSandbox"
          description: |
            A sandbox create body. Its `image` is an OCI reference or Dockerfile build object,
            optionally with context_artifact_id. A missing image, provider_default, an existing
            provider artifact has nothing to prepare and is refused. Only
            declared prepared source/context variants are eligible. A private_oci image is
            prepared with the body's registry_credentials, only on routes that use your own
            provider keys.
        top:
          type: integer
          minimum: 1
          default: 1
          description: |
            With `match`, how many of the best-ranked distinct providers to prepare on. Fewer
            are prepared when fewer providers can run the create.
      allOf:
        - oneOf:
            - required:
                - providers
            - required:
                - match
        - if:
            required:
              - providers
          then:
            required:
              - image
        - if:
            required:
              - image
          then:
            required:
              - providers
        - if:
            required:
              - sizes
          then:
            required:
              - providers
        - if:
            required:
              - registry_credentials
          then:
            required:
              - providers
        - if:
            required:
              - top
          then:
            required:
              - match
    ImageProviders:
      description: Provider names or all providers configured for this workspace.
      oneOf:
        - type: array
          minItems: 1
          uniqueItems: true
          items:
            type: string
            minLength: 1
        - type: string
          const: all
    MatchSandbox:
      allOf:
        - $ref: "#/components/schemas/CreateSandbox"
        - not:
            type: object
            properties:
              secret_values: {}
            required:
              - secret_values
      description: |
        The creation request shape without request-only secret values. registry_credentials
        is accepted, as on create, so a private_oci body can be sent unchanged to match.
    CreateSandbox:
      type: object
      additionalProperties: false
      description: |
        A creation body: gates by name in requirements, a lifetime budget in lifetime_seconds,
        and a volume checkpoint to restore. Every field is optional; {} is a baseline sandbox in
        the standard image that may run for 300 seconds, or for as long as the credit balance
        covers it on a credit-funded route that can extend a lifetime. The words of the retired 0.2.0
        request (timeout_seconds, egress, provider, a dotted requirements key) are refused
        with invalid_request, whose fix names the word to send instead. Creation controls are
        provider-independent: use resources, lifetime_seconds, at_end, image, env, labels,
        requirements and routing preferences. Provider-native creation options are not accepted.
      properties:
        lifetime_seconds:
          type:
            - integer
            - "null"
          minimum: 60
          description: |
            The sandbox's lifetime in seconds, starting when BoxRouter begins creation on the
            selected route. Creation time counts, so less time may remain when the sandbox is
            ready. The minimum is 60 seconds. The requested value is matched against the route's
            max_lifetime_seconds, and a credit-funded sandbox reserves credits for all of it.

            Leave it out for a credit-funded sandbox on a route that can extend a lifetime
            (`extendable_lifetime`) and the sandbox runs for as long as the workspace's credit
            balance covers it: credits are reserved one hour at a time and renewed before each
            hour ends. `funded_until` on the sandbox says when the current reservation ends; the
            sandbox ends then if the balance could not cover another hour. Left out on any other
            create (a route that cannot extend a lifetime, or a sandbox paid with the workspace's
            own provider account), it is 300 seconds.

            Null asks for a sandbox with no lifetime: it runs until you destroy it. Only a provider
            that never ends a sandbox serves it, and only on the workspace's own provider account;
            leave at_end out with it.
        at_end:
          type: string
          enum:
            - destroy
            - pause
          default: destroy
          description: |
            What happens when the lifetime is used up. pause needs pause_resume in requirements.
            Refused with invalid_request when lifetime_seconds is null, because nothing ends that
            sandbox.
        requirements:
          $ref: "#/components/schemas/SandboxProviderSpecRequirements"
          x-boxrouter-sdk-list-shorthand: {}
        resources:
          $ref: "#/components/schemas/SandboxProviderSpecResources"
        preferences:
          $ref: "#/components/schemas/ProviderPreferences"
        privacy:
          $ref: "#/components/schemas/PrivacyPolicy"
        route:
          $ref: "#/components/schemas/CreationRouteFilter"
        volume:
          $ref: "#/components/schemas/AttachVolume"
        image:
          $ref: "#/components/schemas/SandboxImageSelection"
          description: |
            Omitted selects BoxRouter's standard image. "provider_default" opts out and runs
            the provider's own default. Any other string is an OCI image reference. Use a
            one-key dockerfile or provider_artifact object for those artifact sources; the
            latter requires exactly one value in preferences.only. Use {private_oci} for an image whose registry
            needs a login, together with registry_credentials; it runs only on routes that use
            your own provider keys. Every selected image must contain bash and
            sha256sum.
        registry_credentials:
          type: object
          additionalProperties: false
          required:
            - username
            - password
          properties:
            username:
              type: string
              minLength: 1
              pattern: ^[^\u0000]*$
            password:
              type: string
              minLength: 1
              writeOnly: true
              pattern: ^[^\u0000]*$
          description: |
            Request-only login for the registry named by a private_oci image; required with it and
            refused without it. Both strings are non-empty, at most 64 KiB UTF-8 bytes together
            (enforced outside JSON Schema). It is passed to the provider to set up a copy of the
            image and is never stored, logged, traced, returned or included in idempotency records.
        env:
          type: object
          maxProperties: 256
          additionalProperties:
            type: string
          propertyNames:
            pattern: ^[A-Za-z_][A-Za-z0-9_]*$
          description: |
            Creation-time environment, limited by the server to 64 KiB total UTF-8 bytes. This byte
            limit is enforced outside JSON Schema validation. Never returned, logged or stored.
        secret_refs:
          $ref: "#/components/schemas/AdditionalSecretReferences"
          description: Explicit saved workspace secret references by destination environment name.
        secret_values:
          type: object
          minProperties: 1
          maxProperties: 256
          propertyNames:
            $ref: "#/components/schemas/SecretName"
          additionalProperties:
            type: string
            writeOnly: true
            maxLength: 16384
            pattern: ^[^\u0000]*$
          description: |
            Request-only values delivered through the hidden_secrets gate. Values are sent once
            during sandbox creation. The server enforces a 16 KiB per-value and 64 KiB total
            UTF-8 byte limit outside JSON Schema validation. Values are never stored, logged,
            traced, returned, or included in idempotency records. Use the matching placeholder
            from the response in configuration.
        labels:
          type: object
          maxProperties: 64
          propertyNames:
            maxLength: 63
          additionalProperties:
            type: string
            maxLength: 255
          description: BoxRouter labels, returned on the sandbox; not implicitly provider tags.
      allOf:
        - if:
            required:
              - secret_values
          then:
            required:
              - requirements
            properties:
              requirements:
                required:
                  - hidden_secrets
        - if:
            required:
              - volume
              - image
          then:
            properties:
              image:
                anyOf:
                  - type: string
                    allOf:
                      - not:
                          const: provider_default
                      - not:
                          pattern: "^prepared:sha256:"
                  - $ref: "#/components/schemas/SandboxImageSelectionProviderArtifact"
        - if:
            required:
              - image
            properties:
              image:
                type: object
                required:
                  - provider_artifact
          then:
            required:
              - preferences
            properties:
              preferences:
                required:
                  - only
                properties:
                  only:
                    maxItems: 1
    SandboxProviderSpecRequirements:
      type: object
      additionalProperties: false
      properties:
        custom_images:
          $ref: "#/components/schemas/SandboxProviderSpecCustomImagesRequirement"
        desktop:
          $ref: "#/components/schemas/SandboxProviderSpecDesktopRequirement"
        hidden_secrets:
          $ref: "#/components/schemas/SandboxProviderSpecHiddenSecretsRequirement"
        idle_shutdown:
          $ref: "#/components/schemas/SandboxProviderSpecIdleShutdownRequirement"
        network_lockdown:
          $ref: "#/components/schemas/SandboxProviderSpecNetworkLockdownRequirement"
        pause_resume:
          $ref: "#/components/schemas/SandboxProviderSpecPauseResumeRequirement"
        previews:
          $ref: "#/components/schemas/SandboxProviderSpecPreviewsRequirement"
        processes:
          $ref: "#/components/schemas/SandboxProviderSpecProcessesRequirement"
        tcp_ports:
          $ref: "#/components/schemas/SandboxProviderSpecTcpPortsRequirement"
        terminal:
          $ref: "#/components/schemas/SandboxProviderSpecTerminalRequirement"
      if:
        required:
          - previews
        properties:
          previews:
            required:
              - survives_pause
      then:
        anyOf:
          - required:
              - pause_resume
          - required:
              - idle_shutdown
      x-boxrouter-sps-profile:
        version: 1
        requirements:
          custom_images: []
          desktop: []
          hidden_secrets:
            - inject_into
            - intercepts
            - transparent
          idle_shutdown:
            - custom_idle_time
            - idle_seconds
          network_lockdown:
            - allow
            - deny
            - policy
          pause_resume:
            - keeps
          previews:
            - access
            - link_lifetime_seconds
            - loopback
            - ports
            - revocable
            - survives_pause
            - websocket
          processes:
            - input
          tcp_ports:
            - ports
            - tls
          terminal: []
        resources:
          - arch
          - cpu_min
          - disk_min_mib
          - extendable_lifetime
          - kernel
          - memory_min_mib
          - paused_retention_seconds
          - process_seconds
          - region
          - run_seconds
      description: |
        The gates this sandbox may use, each holding its knobs and its settings side by side.
        A sandbox may use only the baseline and the gates named here: a call to any other gate
        is refused with not_named, on every adapter. Every gate the spec names is accepted
        here, including the ones no adapter implements yet: whether one can be granted is
        matching's answer, so a gate nothing declares, or no loaded report proves, leaves the
        request without a route rather than making it invalid.
    SandboxProviderSpecCustomImagesRequirement:
      type: object
      additionalProperties: false
      properties: {}
      description: |
        Bring your own environment, which must contain bash and sha256sum.
    SandboxProviderSpecDesktopRequirement:
      type: object
      additionalProperties: false
      properties: {}
    SandboxProviderSpecHiddenSecretsRequirement:
      type: object
      additionalProperties: false
      required:
        - inject_into
      properties:
        transparent:
          type: boolean
        intercepts:
          oneOf:
            - enum:
                - injected_hosts
                - all_https
            - type: array
              minItems: 1
              uniqueItems: true
              items:
                enum:
                  - injected_hosts
                  - all_https
        inject_into:
          type: array
          minItems: 1
          uniqueItems: true
          items:
            $ref: "#/components/schemas/SandboxProviderSpecHostname"
          description: Hostnames to which the credential is sent.
      description: |
        Credentials the sandbox never sees: the adapter adds them to outgoing requests from
        outside it. The values travel like env and are never stored, logged or returned; the
        response shows their names only.
    SandboxProviderSpecIdleShutdownRequirement:
      type: object
      additionalProperties: false
      properties:
        custom_idle_time:
          type: boolean
        idle_seconds:
          type: integer
          minimum: 1
          description: How long the sandbox must be inactive before idle shutdown.
      allOf:
        - if:
            required:
              - idle_seconds
          then:
            properties:
              custom_idle_time:
                const: true
        - if:
            required:
              - idle_seconds
          then:
            required:
              - custom_idle_time
            properties:
              custom_idle_time:
                const: true
      description: |
        End or pause a sandbox nobody is using. No knob must be named.
    SandboxProviderSpecNetworkLockdownRequirement:
      type: object
      additionalProperties: false
      required:
        - policy
      properties:
        policy:
          enum:
            - deny_all
            - allowlist
            - denylist
            - open
        allow:
          type: array
          minItems: 1
          uniqueItems: true
          items:
            type: string
            minLength: 1
          description: |
            Hosts or HTTP rules permitted by the selected network policy. Required for
            allowlist and forbidden with deny_all, denylist, or open.
        deny:
          type: array
          minItems: 1
          uniqueItems: true
          items:
            type: string
            minLength: 1
            pattern: ^[^*]*$
      allOf:
        - if:
            properties:
              policy:
                const: deny_all
            required:
              - policy
          then:
            not:
              anyOf:
                - required:
                    - allow
                - required:
                    - deny
        - if:
            properties:
              policy:
                const: allowlist
            required:
              - policy
          then:
            required:
              - allow
            not:
              required:
                - deny
        - if:
            properties:
              policy:
                const: denylist
            required:
              - policy
          then:
            required:
              - deny
            not:
              required:
                - allow
      description: |
        Limits what the sandbox may connect to, enforced by the provider from outside it.
        Previews still reach the sandbox. policy must be named and is exactly one value:
        an allowlist is not what someone assuming deny-all wants.
    SandboxProviderSpecPauseResumeRequirement:
      type: object
      additionalProperties: false
      properties:
        keeps:
          oneOf:
            - enum:
                - files_only
                - files_and_memory
            - type: array
              minItems: 1
              uniqueItems: true
              items:
                enum:
                  - files_only
                  - files_and_memory
          description: |
            What a pause keeps. A list means any of these values is acceptable and the response
            says which were granted; "any" is not accepted here, because keeps need not be named.
      description: Pause a sandbox and resume it later. {} asks for the gate without naming a knob.
    SandboxProviderSpecPreviewsRequirement:
      type: object
      additionalProperties: false
      required:
        - access
        - ports
      properties:
        access:
          oneOf:
            - enum:
                - any
                - public
                - private
            - type: array
              minItems: 1
              uniqueItems: true
              items:
                enum:
                  - public
                  - private
          description: |
            Which kinds of link this sandbox may open. "any" accepts whatever the route offers,
            and then the open call may not pick a kind.
        revocable:
          type: boolean
        link_lifetime_seconds:
          type: number
          exclusiveMinimum: 0
        websocket:
          type: boolean
        loopback:
          type: boolean
        survives_pause:
          type: boolean
        ports:
          type: array
          minItems: 1
          uniqueItems: true
          items:
            type: integer
            minimum: 1
            maximum: 65535
          description: The ports previews may be opened on; more than max_ports excludes a route.
      description: |
        A URL for a port inside the sandbox. access must be named, because some providers only
        offer public links; ports is the setting handed to the adapter and can exclude one.
    SandboxProviderSpecProcessesRequirement:
      type: object
      additionalProperties: false
      properties:
        input:
          type: boolean
    SandboxProviderSpecTcpPortsRequirement:
      type: object
      additionalProperties: false
      required:
        - tls
        - ports
      properties:
        tls:
          oneOf:
            - enum:
                - any
                - none
            - type: array
              minItems: 1
              uniqueItems: true
              items:
                enum:
                  - none
        ports:
          type: array
          minItems: 1
          uniqueItems: true
          items:
            type: integer
            minimum: 1
            maximum: 65535
    SandboxProviderSpecTerminalRequirement:
      type: object
      additionalProperties: false
      properties: {}
    SandboxImageSelection:
      oneOf:
        - type: string
          const: provider_default
        - type: string
          minLength: 1
          not:
            const: provider_default
          description: OCI image reference.
        - type: object
          additionalProperties: false
          required:
            - dockerfile
          properties:
            dockerfile:
              type: string
              minLength: 1
            context_artifact_id:
              type: string
              minLength: 1
              description: Unexpired workspace-owned uploaded tar context, leased while the image builds.
        - $ref: "#/components/schemas/SandboxImageSelectionProviderArtifact"
        - type: object
          additionalProperties: false
          required:
            - private_oci
          properties:
            private_oci:
              type: string
              minLength: 1
              description: OCI image reference in a registry that needs a login; send registry_credentials beside it.
    SandboxImageSelectionProviderArtifact:
      type: object
      additionalProperties: false
      required:
        - provider_artifact
      properties:
        provider_artifact:
          type: string
          minLength: 1
          description: A provider-native artifact id usable on the selected provider account.
    ImagePreparationStatus:
      type: object
      additionalProperties: false
      required:
        - image
        - providers
      properties:
        image:
          type: string
          description: Resolved OCI reference or the canonical sha256 content selector of a Dockerfile source and context.
        providers:
          type: array
          items:
            $ref: "#/components/schemas/ImageProviderState"
    ImageProviderState:
      type: object
      additionalProperties: false
      required:
        - provider
        - size
        - state
        - reason
      properties:
        provider:
          type: string
          minLength: 1
          description: Installed provider selected through this workspace's authorized offerings.
        state:
          type: string
          enum:
            - queued
            - preparing
            - ready
            - inactive
            - deleting
            - failed
            - unsupported
            - absent
          x-boxrouter-open-enum: true
        size:
          type:
            - integer
            - "null"
          minimum: 1
          description: Requested memory size in GiB, or null when the copy is not size-specific.
        reason:
          type:
            - string
            - "null"
          description: Eligibility or preparation outcome for this provider.
    UnprepareImage:
      type: object
      additionalProperties: false
      required:
        - image
        - providers
      properties:
        image:
          type: string
          minLength: 1
          description: |
            Image reference, `repo@sha256:digest`, or the `sha256:<64 lowercase hex>` content
            selector returned for a Dockerfile build. A content selector reads this workspace's
            recorded copies without contacting a registry. A private tag works when this workspace has
            prepared or created it with registry_credentials before; otherwise name its digest.
        providers:
          $ref: "#/components/schemas/ImageProviders"
    StartProcess:
      type: object
      additionalProperties: false
      required:
        - command
      properties:
        command:
          x-boxrouter-input-error: Command must not contain NUL bytes
          type: string
          pattern: ^[^\u0000]*$
          description: Bash source run in a login shell.
        env:
          x-boxrouter-input-error: Command env requires valid shell variable names and values without NUL
          type:
            - object
            - "null"
          propertyNames:
            pattern: ^[A-Za-z_][A-Za-z0-9_]*(?![\s\S])
          additionalProperties:
            type: string
            pattern: ^[^\u0000]*$
          description: Environment overrides for the command.
        cwd:
          x-boxrouter-input-error: Command cwd must be an absolute sandbox path without NUL bytes
          type:
            - string
            - "null"
          pattern: ^/[^\u0000]*$
          description: Absolute working directory within the sandbox.
        stdin:
          type: boolean
          default: false
          description: |
            Keep stdin open for subsequent input requests. Requires named `processes.input`
            and selected-route `reattached_stdin` metadata; otherwise `stdin: true` is
            refused as `unsupported_feature` before process launch.
        wait_for_port:
          type:
            - integer
            - "null"
          minimum: 1
          maximum: 65535
          description: |
            Return only after this port accepts TCP connections inside the sandbox, so the
            caller needs no readiness loop. Waits at most 30 seconds. If the process exits first,
            or the port is still closed after the wait, the process is ended and the answer is
            409 `port_not_listening` with the output so far in `details`. Null returns as soon
            as the process starts.
    Process:
      type: object
      additionalProperties: false
      required:
        - id
      properties:
        id:
          type: string
          minLength: 1
    ProcessOutput:
      type: object
      additionalProperties: false
      required:
        - stdout_base64
        - stderr_base64
        - next_stdout
        - next_stderr
        - exit_code
      properties:
        stdout_base64:
          type: string
          contentEncoding: base64
        stderr_base64:
          type: string
          contentEncoding: base64
        next_stdout:
          type: integer
          minimum: 0
        next_stderr:
          type: integer
          minimum: 0
        exit_code:
          type:
            - integer
            - "null"
          description: Set only after the process ended and both output streams were read to their ends.
    WriteStdin:
      type: object
      additionalProperties: false
      required:
        - data
      properties:
        data:
          type: string
          description: UTF-8 text to append exactly once; does not implicitly append a newline or send EOF.
    CreditBalance:
      type: object
      additionalProperties: false
      required:
        - mode
        - currency
        - settled_nanodollars
        - held_nanodollars
        - available_nanodollars
        - purchase_bounds_minor
      properties:
        mode:
          type: string
          enum:
            - test
            - live
          description: Stripe environment configured for purchases; live mode remains disabled until accepted for real money.
        currency:
          type: string
          const: usd
        settled_nanodollars:
          type: string
          maxLength: 20
          pattern: ^(0|-?[1-9][0-9]{0,17}|-?[1-8][0-9]{18}|-?9[01][0-9]{17}|-?92[0-1][0-9]{16}|-?922[0-2][0-9]{15}|-?9223[0-2][0-9]{14}|-?92233[0-6][0-9]{13}|-?922337[0-1][0-9]{12}|-?92233720[0-2][0-9]{10}|-?922337203[0-5][0-9]{9}|-?9223372036[0-7][0-9]{8}|-?92233720368[0-4][0-9]{7}|-?922337203685[0-3][0-9]{6}|-?9223372036854[0-6][0-9]{5}|-?92233720368547[0-6][0-9]{4}|-?922337203685477[0-4][0-9]{3}|-?9223372036854775[0-7][0-9]{2}|922337203685477580[0-7]|-922337203685477580[0-8])$
          description: Canonical base-10 signed 64-bit nanodollar amount as a string, preserving exact integer precision across JSON clients. Confirmed purchases and adjustments less settled usage, in billionths of a US dollar. Negative after a refund of spent credit.
        held_nanodollars:
          type: string
          maxLength: 19
          pattern: ^(0|[1-9][0-9]{0,17}|[1-8][0-9]{18}|9[01][0-9]{17}|92[0-1][0-9]{16}|922[0-2][0-9]{15}|9223[0-2][0-9]{14}|92233[0-6][0-9]{13}|922337[0-1][0-9]{12}|92233720[0-2][0-9]{10}|922337203[0-5][0-9]{9}|9223372036[0-7][0-9]{8}|92233720368[0-4][0-9]{7}|922337203685[0-3][0-9]{6}|9223372036854[0-6][0-9]{5}|92233720368547[0-6][0-9]{4}|922337203685477[0-4][0-9]{3}|9223372036854775[0-7][0-9]{2}|922337203685477580[0-7])$
          description: Canonical base-10 nonnegative 64-bit nanodollar amount as a string, preserving exact integer precision across JSON clients. Credit reserved for work whose final cost is not yet settled.
        available_nanodollars:
          type: string
          maxLength: 20
          pattern: ^(0|-?[1-9][0-9]{0,17}|-?[1-8][0-9]{18}|-?9[01][0-9]{17}|-?92[0-1][0-9]{16}|-?922[0-2][0-9]{15}|-?9223[0-2][0-9]{14}|-?92233[0-6][0-9]{13}|-?922337[0-1][0-9]{12}|-?92233720[0-2][0-9]{10}|-?922337203[0-5][0-9]{9}|-?9223372036[0-7][0-9]{8}|-?92233720368[0-4][0-9]{7}|-?922337203685[0-3][0-9]{6}|-?9223372036854[0-6][0-9]{5}|-?92233720368547[0-6][0-9]{4}|-?922337203685477[0-4][0-9]{3}|-?9223372036854775[0-7][0-9]{2}|922337203685477580[0-7]|-922337203685477580[0-8])$
          description: Canonical base-10 signed 64-bit nanodollar amount as a string, preserving exact integer precision across JSON clients; equals settled_nanodollars minus held_nanodollars.
        held_sandbox_count:
          type: integer
          minimum: 0
          description: How many sandboxes hold a reservation that makes up `held_nanodollars`; a create whose outcome is not yet known counts as one until it resolves. Absent from responses of a Cloud that does not report it yet.
        spent_today_nanodollars:
          type: string
          maxLength: 19
          pattern: ^(0|[1-9][0-9]{0,17}|[1-8][0-9]{18}|9[01][0-9]{17}|92[0-1][0-9]{16}|922[0-2][0-9]{15}|9223[0-2][0-9]{14}|92233[0-6][0-9]{13}|922337[0-1][0-9]{12}|92233720[0-2][0-9]{10}|922337203[0-5][0-9]{9}|9223372036[0-7][0-9]{8}|92233720368[0-4][0-9]{7}|922337203685[0-3][0-9]{6}|9223372036854[0-6][0-9]{5}|92233720368547[0-6][0-9]{4}|922337203685477[0-4][0-9]{3}|9223372036854775[0-7][0-9]{2}|922337203685477580[0-7])$
          description: Managed sandbox charges settled since 00:00 UTC today, for the whole organization. Open reservations are not included; they show in `held_nanodollars`. Absent from responses of a Cloud that does not report it yet.
        spent_this_month_nanodollars:
          type: string
          maxLength: 19
          pattern: ^(0|[1-9][0-9]{0,17}|[1-8][0-9]{18}|9[01][0-9]{17}|92[0-1][0-9]{16}|922[0-2][0-9]{15}|9223[0-2][0-9]{14}|92233[0-6][0-9]{13}|922337[0-1][0-9]{12}|92233720[0-2][0-9]{10}|922337203[0-5][0-9]{9}|9223372036[0-7][0-9]{8}|92233720368[0-4][0-9]{7}|922337203685[0-3][0-9]{6}|9223372036854[0-6][0-9]{5}|92233720368547[0-6][0-9]{4}|922337203685477[0-4][0-9]{3}|9223372036854775[0-7][0-9]{2}|922337203685477580[0-7])$
          description: Managed sandbox charges settled since the first of this month, 00:00 UTC, for the whole organization. Absent from responses of a Cloud that does not report it yet.
        workspace_limit:
          description: Headroom under the requesting workspace's spend limit; null when the workspace has none. Absent from responses of a Cloud that does not report it yet.
          oneOf:
            - type: "null"
            - type: object
              additionalProperties: false
              required:
                - limit_source
                - period
                - limit_nanodollars
                - spent_nanodollars
                - remaining_nanodollars
                - reset_at
              properties:
                limit_source:
                  type: string
                  description: Names what the limit is; the same value a spend_limit_exceeded refusal carries in details.limit_source.
                period:
                  $ref: "#/components/schemas/SpendLimitPeriod"
                limit_nanodollars:
                  type: string
                  description: The cap, as an exact nanodollar string.
                spent_nanodollars:
                  type: string
                  description: Settled charges plus open reservations counted against the cap in its period.
                remaining_nanodollars:
                  type: string
                  description: The cap minus what is counted; never below zero.
                reset_at:
                  type:
                    - string
                    - "null"
                  format: date-time
                  description: When the period next restarts in UTC; null for a total cap.
        purchase_bounds_minor:
          type: object
          additionalProperties: false
          required:
            - minimum
            - maximum
          properties:
            minimum:
              type: integer
              minimum: 1
              description: Smallest accepted top-up in US cents.
            maximum:
              type: integer
              minimum: 1
              description: Largest accepted top-up in US cents.
    SpendLimitPeriod:
      type: string
      enum:
        - total
        - daily
        - monthly
    SpendLimitList:
      type: object
      additionalProperties: false
      required:
        - mode
        - workspace
      properties:
        mode:
          type: string
          enum:
            - test
            - live
          description: Billing book mode these limits apply to.
        workspace:
          oneOf:
            - $ref: "#/components/schemas/SpendLimit"
            - type: "null"
        workspace_spent_nanodollars:
          description: Managed usage counted against the workspace cap so far in its current period, holds included, as a canonical base-10 nonnegative 64-bit nanodollar string. Null when no workspace cap is set.
          oneOf:
            - type: string
              maxLength: 19
              pattern: ^(0|[1-9][0-9]{0,17}|[1-8][0-9]{18}|9[01][0-9]{17}|92[0-1][0-9]{16}|922[0-2][0-9]{15}|9223[0-2][0-9]{14}|92233[0-6][0-9]{13}|922337[0-1][0-9]{12}|92233720[0-2][0-9]{10}|922337203[0-5][0-9]{9}|9223372036[0-7][0-9]{8}|92233720368[0-4][0-9]{7}|922337203685[0-3][0-9]{6}|9223372036854[0-6][0-9]{5}|92233720368547[0-6][0-9]{4}|922337203685477[0-4][0-9]{3}|9223372036854775[0-7][0-9]{2}|922337203685477580[0-7])$
            - type: "null"
    SpendLimit:
      type: object
      additionalProperties: false
      required:
        - limit_usd
        - limit_period
      properties:
        limit_usd:
          $ref: "#/components/schemas/SpendLimitUsd"
        limit_period:
          $ref: "#/components/schemas/SpendLimitPeriod"
      description: A cap on managed compute spend. Both fields are always set; clear a cap with the delete operation.
    SpendLimitUsd:
      type: string
      pattern: ^(?:0\.[0-9]*[1-9][0-9]{0,8}|[1-9][0-9]*(?:\.[0-9]{1,9})?)$
      description: Positive exact USD amount as a decimal string with at most nine fractional digits.
    SpendLimitRecord:
      type: object
      additionalProperties: false
      required:
        - mode
        - scope
        - spend_limit
      properties:
        mode:
          type: string
          enum:
            - test
            - live
        scope:
          type: string
          enum:
            - workspace
        spend_limit:
          oneOf:
            - $ref: "#/components/schemas/SpendLimit"
            - type: "null"
    CreateCreditPurchase:
      type: object
      additionalProperties: false
      required:
        - amount_minor
      properties:
        amount_minor:
          type: integer
          minimum: 1
          description: Top-up in whole US cents, within purchase_bounds_minor.
        expected_total_minor:
          type: integer
          minimum: 1
          description: Required for in-page card purchases; the total from the latest quote. A changed total returns 409 quote_changed with the current quote.
        payment_method_id:
          type: string
          pattern: ^pm_[A-Za-z0-9_]+$
          description: Optional saved card to charge immediately. Omit to use hosted Checkout.
        success_url:
          type: string
          format: uri
          maxLength: 2048
          description: URL on the BoxRouter website the payer returns to after paying; other origins are rejected. `{PURCHASE_ID}` is replaced. Returning here does not establish payment.
        cancel_url:
          type: string
          format: uri
          maxLength: 2048
          description: URL on the BoxRouter website the payer returns to after abandoning Checkout; other origins are rejected. `{PURCHASE_ID}` is replaced.
      anyOf:
        - required:
            - payment_method_id
          properties:
            payment_method_id:
              type: string
              pattern: ^pm_[A-Za-z0-9_]+$
        - required:
            - success_url
            - cancel_url
          properties:
            success_url:
              $ref: "#/components/schemas/success_url"
            cancel_url:
              $ref: "#/components/schemas/cancel_url"
    success_url:
      type: string
      format: uri
      maxLength: 2048
      description: URL on the BoxRouter website the payer returns to after paying; other origins are rejected. `{PURCHASE_ID}` is replaced. Returning here does not establish payment.
    cancel_url:
      type: string
      format: uri
      maxLength: 2048
      description: URL on the BoxRouter website the payer returns to after abandoning Checkout; other origins are rejected. `{PURCHASE_ID}` is replaced.
    CreatedCreditPurchase:
      oneOf:
        - $ref: "#/components/schemas/StartedCreditPurchase"
        - $ref: "#/components/schemas/AuthenticatedCreditPurchase"
        - $ref: "#/components/schemas/ConfirmedSavedCardCreditPurchase"
    StartedCreditPurchase:
      allOf:
        - $ref: "#/components/schemas/CreditPurchase"
        - type: object
          required:
            - checkout_url
          properties:
            status:
              type: string
              const: pending
            method:
              type: string
              const: checkout
            checkout_url:
              type: string
              format: uri
              description: Hosted payment page for this purchase. Send the payer here.
          not:
            required:
              - authentication_url
            properties:
              authentication_url: true
    CreditPurchase:
      type: object
      required:
        - id
        - status
        - amount_minor
        - currency
        - created_at
        - updated_at
      properties:
        id:
          type: string
        status:
          type: string
          description: |
            pending until the payment provider's verified notification arrives; confirmed
            once the payment has been verified; canceled when an unpaid Checkout session
            expires; failed when a saved-card attempt is definitively declined. Later
            refunds and disputes are separate adjustments and do not change this status.
            Clients must handle unknown values explicitly.
        amount_minor:
          type: integer
          minimum: 1
          description: Credit added to the balance, in US cents. The purchase fee is never part of it.
        fee_minor:
          type: integer
          minimum: 0
          description: BoxRouter's fee for adding credits, in US cents, charged on top of amount_minor and never credited. Zero when the purchase carried no fee. Absent on older services.
        total_minor:
          type: integer
          minimum: 1
          description: amount_minor plus fee_minor, in US cents, before any tax. This is what the payer is asked to pay. Absent on older services.
        currency:
          type: string
          const: usd
        method:
          type: string
          enum:
            - checkout
            - saved_card
            - auto_top_up
          description: How this purchase was started. New services report existing purchases as checkout; absent on older services.
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        failure_reason:
          type: string
          minLength: 1
          maxLength: 100
          description: Short safe description present only when a saved-card attempt failed.
        adjustments:
          type: array
          description: Later refunds, dispute adjustments and verified dispute outcomes, oldest first.
          items:
            $ref: "#/components/schemas/CreditAdjustment"
        invoice:
          $ref: "#/components/schemas/CreditInvoice"
          description: Present only after payment is confirmed and Stripe provides invoice links. Older purchases can have no invoice.
        receipt_url:
          type: string
          format: uri
          description: Stripe receipt for the confirmed payment; never returned for an unpaid purchase. Links can change.
        refund:
          $ref: "#/components/schemas/CreditPurchaseRefund"
          description: Self-serve refund eligibility and the newest refund. Present only when refund routes are enabled; never contains a stale quote.
      allOf:
        - if:
            properties:
              status:
                not:
                  const: confirmed
            required:
              - status
          then:
            not:
              anyOf:
                - required:
                    - invoice
                  properties:
                    invoice: true
                - required:
                    - receipt_url
                  properties:
                    receipt_url: true
        - if:
            properties:
              status:
                const: failed
            required:
              - status
          then:
            properties:
              failure_reason:
                $ref: "#/components/schemas/failure_reason"
            required:
              - failure_reason
        - if:
            properties:
              status:
                not:
                  const: failed
            required:
              - status
          then:
            not:
              required:
                - failure_reason
              properties:
                failure_reason: true
    CreditAdjustment:
      type: object
      required:
        - kind
        - amount_minor
        - created_at
      properties:
        kind:
          type: string
          enum:
            - refund
            - customer_refund
            - dispute_opened
            - dispute_won
            - dispute_reinstated
            - dispute_lost
          x-boxrouter-open-enum: true
          description: |
            Refund or dispute history. dispute_reinstated is an exact positive restoration of
            funds returned by Stripe. dispute_lost is a verified final outcome marker and does
            not change the credit balance.
        amount_minor:
          type: integer
          description: |
            Signed amount in US cents; negative reduces the balance and positive restores it.
            A dispute_reinstated adjustment is positive; a dispute_lost outcome marker has amount zero.
        created_at:
          type: string
          format: date-time
      allOf:
        - if:
            properties:
              kind:
                const: dispute_reinstated
            required:
              - kind
          then:
            properties:
              amount_minor:
                type: integer
                minimum: 1
        - if:
            properties:
              kind:
                const: dispute_lost
            required:
              - kind
          then:
            properties:
              amount_minor:
                const: 0
    CreditInvoice:
      type: object
      additionalProperties: false
      required:
        - number
        - pdf_url
        - hosted_url
      properties:
        number:
          type: string
          minLength: 1
        pdf_url:
          type: string
          format: uri
        hosted_url:
          type: string
          format: uri
    CreditPurchaseRefund:
      type: object
      required:
        - state
      properties:
        state:
          type: string
          enum:
            - available
            - unavailable
            - pending
            - succeeded
            - failed
            - failed_after_success
          x-boxrouter-open-enum: true
          description: Current refund state; clients must tolerate future values.
        disabled_reason:
          type: string
          enum:
            - window_closed
            - below_minimum
            - nothing_to_refund
            - not_card
            - no_invoice
            - dispute_open
            - fraud_review
            - operator_refunded
            - purchase_in_progress
            - fee_unavailable
            - legacy_purchase
            - already_refunded
            - daily_limit
            - retry_requires_support
          x-boxrouter-open-enum: true
          description: Why a refund cannot currently be requested.
        refundable_until:
          type: string
          format: date-time
          description: Exclusive 24-hour deadline from payment confirmation, in UTC.
        fee_minor:
          type: integer
          minimum: 0
          description: Stripe processing fee in US cents, which is not refunded.
        latest_refund:
          $ref: "#/components/schemas/CreditRefundSummary"
          description: Most recent refund, including pending or failed refunds.
    CreditRefundSummary:
      type: object
      required:
        - id
        - status
        - unused
        - total
        - fee
        - card
        - requested_at
      properties:
        id:
          type: string
        status:
          type: string
          enum:
            - pending
            - succeeded
            - failed
            - failed_after_success
          x-boxrouter-open-enum: true
        unused:
          type: integer
          minimum: 0
          description: Credit debited from the balance in US cents.
        total:
          type: integer
          minimum: 0
          description: Stripe's total refund including tax, in US cents.
        fee:
          type: integer
          minimum: 0
          description: Original Stripe processing fee retained, in US cents.
        card:
          $ref: "#/components/schemas/RefundCard"
        requested_at:
          type: string
          format: date-time
    RefundCard:
      type: object
      additionalProperties: false
      required:
        - type
      properties:
        brand:
          type: string
        last4:
          type: string
          pattern: ^[0-9]{4}$
          description: Last four digits of the refunded card; absent for a Link refund, which has no card number.
        type:
          type: string
          enum:
            - card
            - link
          x-boxrouter-open-enum: true
      allOf:
        - if:
            properties:
              type:
                const: card
            required:
              - type
          then:
            required:
              - brand
              - last4
        - if:
            properties:
              type:
                const: link
            required:
              - type
          then:
            not:
              required:
                - last4
              properties:
                last4: true
    failure_reason:
      type: string
      minLength: 1
      maxLength: 100
      description: Short safe description present only when a saved-card attempt failed.
    AuthenticatedCreditPurchase:
      allOf:
        - $ref: "#/components/schemas/CreditPurchase"
        - type: object
          required:
            - authentication_url
          properties:
            status:
              type: string
              const: pending
            method:
              type: string
              const: saved_card
            authentication_url:
              type: string
              format: uri
              description: Stripe-hosted invoice page for required authentication; poll the purchase after returning to the dashboard.
            payment_intent_client_secret:
              type: string
              minLength: 1
              description: Stripe PaymentIntent client secret for in-page confirmation when 3DS is required.
          not:
            required:
              - checkout_url
            properties:
              checkout_url: true
    ConfirmedSavedCardCreditPurchase:
      allOf:
        - $ref: "#/components/schemas/CreditPurchase"
        - type: object
          properties:
            status:
              type: string
              const: confirmed
            method:
              type: string
              const: saved_card
          not:
            anyOf:
              - required:
                  - checkout_url
                properties:
                  checkout_url: true
              - required:
                  - authentication_url
                properties:
                  authentication_url: true
    CreditPurchaseDeclined:
      allOf:
        - $ref: "#/components/schemas/ApiErrorResponse"
        - type: object
          properties:
            error:
              type: object
              required:
                - details
              properties:
                code:
                  type: string
                  const: payment_declined
                outcome:
                  type: string
                  const: not_started
                details:
                  type: object
                  required:
                    - purchase_id
                    - failure_reason
                  properties:
                    purchase_id:
                      type: string
                      minLength: 1
                    failure_reason:
                      type: string
                      minLength: 1
                      maxLength: 100
    CreditPurchaseList:
      type: object
      required:
        - data
        - next_cursor
      properties:
        data:
          type: array
          description: Workspace purchases ordered by creation time and ID descending.
          items:
            $ref: "#/components/schemas/CreditPurchase"
        next_cursor:
          type:
            - string
            - "null"
          description: Opaque cursor for the next page; null at the end. Scoped to this workspace.
    CreditPurchaseQuoteRequest:
      type: object
      additionalProperties: false
      required:
        - amount_minor
      properties:
        amount_minor:
          type: integer
          minimum: 1
          description: Top-up principal in whole US cents.
    ManagedCreditUsageList:
      type: object
      additionalProperties: false
      required:
        - data
        - next_cursor
      properties:
        data:
          type: array
          description: Managed-compute credit movements, newest first. Settled nonzero charges, later corrections to them, reservations still held, and reservations released with no charge. BYOK work and internal cost evidence are excluded.
          items:
            $ref: "#/components/schemas/ManagedCreditUsage"
        next_cursor:
          type:
            - string
            - "null"
          minLength: 1
          maxLength: 512
          description: Opaque workspace-scoped continuation cursor; null when no older charges remain.
    ManagedCreditUsage:
      type: object
      additionalProperties: false
      required:
        - id
        - amount_nanodollars
        - created_at
      properties:
        id:
          type: string
          pattern: ^[0-9a-f]{24}$
          description: Stable opaque activity ID; it does not reveal the internal reservation ID.
        kind:
          type: string
          enum:
            - sandbox_charge
            - sandbox_correction
            - sandbox_hold
            - sandbox_release
          x-boxrouter-open-enum: true
          description: |
            `sandbox_charge`: a sandbox's charge as first settled. `sandbox_correction`: a later
            change to that charge. `sandbox_hold`: a reservation still held against the balance.
            `sandbox_release`: a reservation released with no charge. Absent from responses of a
            Cloud that does not report it yet, where every row is a `sandbox_charge`. Clients must
            handle unknown values explicitly.
        sandbox_id:
          type:
            - string
            - "null"
          minLength: 1
          maxLength: 128
          description: The sandbox this row belongs to. Null when the sandbox cannot be identified. Absent where `kind` is absent.
        sandbox_name:
          type:
            - string
            - "null"
          description: The sandbox's name label, when it has one. Null otherwise. Absent where `kind` is absent.
        key_id:
          type:
            - string
            - "null"
          description: The workspace API key whose request the row belongs to. Null when the work was not made through an API key, such as a dashboard action, or the key can no longer be identified. Absent where `kind` is absent and on a Cloud that does not report it yet.
        amount_nanodollars:
          type: string
          maxLength: 20
          pattern: ^(-?[1-9][0-9]{0,18})$
          description: |
            The credit the row moved, in billionths of a US dollar, never including the provider's
            cost. For a `sandbox_charge` it is the positive debit. For a `sandbox_correction` it is
            the change to the charge: positive when the charge went up and the balance was debited,
            negative when it went down and the balance was credited. For a `sandbox_hold` it is the
            credit currently reserved, positive. For a `sandbox_release` it is the reserved credit
            returned, positive.
        created_at:
          type: string
          format: date-time
          description: Time the debit settled, the correction was recorded, the reservation last changed, or the reservation was released.
    UsageReportPage:
      type: object
      additionalProperties: false
      required:
        - as_of
        - data
        - next_cursor
      properties:
        as_of:
          type: string
          format: date-time
          description: UTC timestamp of the repeatable-read snapshot used to compose this page.
        data:
          type: array
          items:
            $ref: "#/components/schemas/UsageReportRow"
        next_cursor:
          type:
            - string
            - "null"
          minLength: 1
          maxLength: 512
    UsageReportRow:
      type: object
      additionalProperties: false
      required:
        - period
        - group_key
        - sandbox_count
        - compute_seconds
        - compute_seconds_unknown_sandboxes
        - charge_usd
        - held_usd
        - byok_provider_cost_usd
        - byok_provider_cost_unknown_sandboxes
      properties:
        period:
          type: string
          minLength: 1
          description: Inclusive UTC date range for non-day groups, UTC day for day groups, or `current` for the current open-hold snapshot.
        group_key:
          type:
            - string
            - "null"
          minLength: 1
          description: Provider, UTC day (or `current`), API-key ID, or sandbox ID; null means the source key could not be attributed.
        sandbox_count:
          type: integer
          minimum: 1
        compute_seconds:
          type:
            - object
            - "null"
          additionalProperties: false
          required:
            - low
            - high
          properties:
            low:
              $ref: "#/components/schemas/Seconds"
            high:
              $ref: "#/components/schemas/Seconds"
          description: Exact conservative interval bounds in seconds; null when no computable interval is present or any contributing duration is unknown.
        compute_seconds_unknown_sandboxes:
          type: integer
          minimum: 0
          description: Sandboxes with missing or uncertain compute-duration evidence.
        charge_usd:
          $ref: "#/components/schemas/Usd"
          description: Exact settled managed-compute charge recognized within this row.
        held_usd:
          $ref: "#/components/schemas/Usd"
          description: Current managed reservation amount; this is a snapshot and may change on the next request.
        byok_provider_cost_usd:
          oneOf:
            - $ref: "#/components/schemas/Usd"
            - type: "null"
          description: Exact BYOK provider cost in USD, from each sandbox's provider-reported cost or its provider usage records priced at list rates. Null when any contributing BYOK sandbox has an unknown provider cost; partial subtotals and local-clock costs are never returned. This is separate from the BoxRouter customer charge.
        byok_provider_cost_unknown_sandboxes:
          type: integer
          minimum: 0
          description: BYOK sandboxes whose provider cost is unknown. Any positive count makes byok_provider_cost_usd null.
        pending_holds:
          type: object
          additionalProperties: false
          required:
            - count
            - oldest_age_seconds
            - cause
          properties:
            count:
              type: integer
              minimum: 1
              description: Managed holds in this row whose create has no known sandbox identity yet. They keep counting in `held_usd` until an operator binds or settles them.
            oldest_age_seconds:
              type:
                - integer
                - "null"
              minimum: 0
              description: Seconds since the oldest such hold was reserved; null when unknown.
            cause:
              type: string
              enum:
                - create_outcome_unknown
              x-boxrouter-open-enum: true
              description: Why the holds are pending. Clients must tolerate causes they do not know.
          description: Present only when the row includes such holds; absent otherwise. Absence means none, not zero.
      allOf:
        - if:
            properties:
              byok_provider_cost_unknown_sandboxes:
                minimum: 1
            required:
              - byok_provider_cost_unknown_sandboxes
          then:
            properties:
              byok_provider_cost_usd:
                type: "null"
    Seconds:
      type: string
      pattern: ^(0|[1-9][0-9]*)(\.[0-9]*[1-9])?$
      description: Exact decimal string; never rounded through a JSON floating-point number.
    Usd:
      type: string
      pattern: ^(0|[1-9][0-9]*)\.[0-9]{9}$
      description: Exact USD decimal string with nanodollar precision.
    CancelCreditPurchase:
      type: object
      additionalProperties: false
      properties:
        success_url:
          type: string
          format: uri
          maxLength: 2048
          description: BoxRouter website URL for a recovered Checkout session.
        cancel_url:
          type: string
          format: uri
          maxLength: 2048
          description: BoxRouter website URL used if recovered Checkout is canceled.
    RedeemCreditPromotion:
      type: object
      additionalProperties: false
      required:
        - code
      properties:
        code:
          type: string
          minLength: 1
          maxLength: 128
          description: The promotion code as the customer typed it.
    CreditPromotionList:
      type: object
      additionalProperties: false
      required:
        - data
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/CreditPromotion"
    BillingDetails:
      type: object
      additionalProperties: false
      required:
        - name
        - email
        - address
        - tax_id
      properties:
        name:
          type:
            - string
            - "null"
        email:
          type:
            - string
            - "null"
        address:
          oneOf:
            - $ref: "#/components/schemas/BillingAddress"
            - type: "null"
        tax_id:
          oneOf:
            - $ref: "#/components/schemas/BillingTaxId"
            - type: "null"
    BillingAddress:
      type: object
      additionalProperties: false
      required:
        - line1
        - line2
        - city
        - state
        - postal_code
        - country
      properties:
        line1:
          type:
            - string
            - "null"
          maxLength: 200
        line2:
          type:
            - string
            - "null"
          maxLength: 200
        city:
          type:
            - string
            - "null"
          maxLength: 200
        state:
          type:
            - string
            - "null"
          maxLength: 200
        postal_code:
          type:
            - string
            - "null"
          maxLength: 50
        country:
          type:
            - string
            - "null"
          pattern: ^[A-Z]{2}$
          description: ISO 3166-1 alpha-2 country code, or null when unset.
    BillingTaxId:
      type: object
      additionalProperties: false
      required:
        - type
        - value
      properties:
        type:
          type: string
          description: Stripe tax ID type; unknown future types are returned unchanged.
        value:
          type: string
        verification_status:
          type: string
          description: Stripe verification status when available (pending, verified, unverified or unavailable); clients must handle unknown values.
    UpdateBillingDetails:
      type: object
      additionalProperties: false
      required:
        - name
        - email
        - address
      properties:
        name:
          type:
            - string
            - "null"
          minLength: 1
          maxLength: 256
        email:
          type:
            - string
            - "null"
          format: email
          maxLength: 512
        address:
          oneOf:
            - $ref: "#/components/schemas/BillingAddress"
            - type: "null"
        tax_id:
          oneOf:
            - $ref: "#/components/schemas/BillingTaxIdInput"
            - type: "null"
          description: One business tax ID. Null or omission removes existing customer tax IDs; changes apply to future invoices.
    BillingTaxIdInput:
      type: object
      additionalProperties: false
      required:
        - type
        - value
      properties:
        type:
          type: string
          enum:
            - ad_nrt
            - ae_trn
            - al_tin
            - am_tin
            - ao_tin
            - ar_cuit
            - au_abn
            - au_arn
            - aw_tin
            - az_tin
            - ba_tin
            - bb_tin
            - bd_bin
            - bf_ifu
            - bg_uic
            - bh_vat
            - bj_ifu
            - bo_tin
            - br_cnpj
            - br_cpf
            - bs_tin
            - by_tin
            - ca_bn
            - ca_gst_hst
            - ca_pst_bc
            - ca_pst_mb
            - ca_pst_sk
            - ca_qst
            - cd_nif
            - ch_uid
            - ch_vat
            - cl_tin
            - cm_niu
            - cn_tin
            - co_nit
            - cr_tin
            - cv_nif
            - de_stn
            - do_rcn
            - ec_ruc
            - eg_tin
            - es_cif
            - et_tin
            - eu_oss_vat
            - eu_vat
            - fo_vat
            - gb_vat
            - ge_vat
            - gi_tin
            - gn_nif
            - hk_br
            - hr_oib
            - hu_tin
            - ic_nif
            - id_npwp
            - il_vat
            - in_gst
            - is_vat
            - it_cf
            - jp_cn
            - jp_rn
            - jp_trn
            - ke_pin
            - kg_tin
            - kh_tin
            - kr_brn
            - kz_bin
            - la_tin
            - li_uid
            - li_vat
            - lk_vat
            - ma_vat
            - md_vat
            - me_pib
            - mk_vat
            - mr_nif
            - mx_rfc
            - my_frp
            - my_itn
            - my_sst
            - ng_tin
            - no_vat
            - no_voec
            - np_pan
            - nz_gst
            - om_vat
            - pe_ruc
            - ph_tin
            - pl_nip
            - py_ruc
            - ro_tin
            - rs_pib
            - ru_inn
            - ru_kpp
            - sa_vat
            - sg_gst
            - sg_uen
            - si_tin
            - sn_ninea
            - sr_fin
            - sv_nit
            - th_vat
            - tj_tin
            - tr_tin
            - tw_vat
            - tz_vat
            - ua_vat
            - ug_tin
            - us_ein
            - uy_ruc
            - uz_tin
            - uz_vat
            - ve_rif
            - vn_tin
            - za_vat
            - zm_tin
            - zw_tin
          description: Customer tax ID type accepted by Stripe. See https://docs.stripe.com/api/tax_ids/customer_create.
        value:
          type: string
          minLength: 1
          maxLength: 100
        verification_status:
          type: string
          readOnly: true
          description: Ignored on update; supplied by Stripe on reads.
    CreditAlerts:
      type: object
      additionalProperties: false
      required:
        - low_balance_threshold_minor
        - email_delivery
      properties:
        low_balance_threshold_minor:
          type:
            - integer
            - "null"
          minimum: 1
          maximum: 9007199254740991
          description: Optional low-balance alert level in US cents; null disables it. Evaluating a balance below a newly enabled level alerts immediately. Zero-balance alerts are automatic. Alerts re-arm when balance recovers to the level (or above zero).
        email_delivery:
          type: string
          enum:
            - configured
            - log_only
          description: Whether SMTP and a verified workspace-owner address are configured for low- and zero-balance notices; configured does not guarantee successful delivery.
    CreditAlertsUpdate:
      type: object
      additionalProperties: false
      required:
        - low_balance_threshold_minor
      properties:
        low_balance_threshold_minor:
          type:
            - integer
            - "null"
          minimum: 1
          maximum: 9007199254740991
          description: Optional low-balance alert level in US cents; null disables it. Evaluating a balance below a newly enabled level alerts immediately. Zero-balance alerts are automatic. Alerts re-arm when balance recovers to the level (or above zero).
    CreditPaymentMethodList:
      type: object
      additionalProperties: false
      required:
        - data
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/CreditPaymentMethod"
    CreditPaymentMethod:
      type: object
      additionalProperties: false
      required:
        - id
      properties:
        id:
          type: string
          pattern: ^pm_[A-Za-z0-9_]+$
        brand:
          type:
            - string
            - "null"
          description: Card brand; absent for Link methods.
        last4:
          type:
            - string
            - "null"
          pattern: ^[0-9]{4}$
          description: Last four card digits; absent for Link methods.
        exp_month:
          type:
            - integer
            - "null"
          minimum: 1
          maximum: 12
          description: Card expiry month; absent for Link methods.
        exp_year:
          type:
            - integer
            - "null"
          minimum: 2000
          maximum: 9999
          description: Card expiry year; absent for Link methods.
        default:
          type: boolean
          description: True for the workspace's one default saved card; absent on older services.
        priority:
          type: integer
          minimum: 1
          description: One-based charge order for automatic top-up; lower priorities are tried first.
        type:
          type: string
          enum:
            - card
            - link
          x-boxrouter-open-enum: true
          description: Payment method type. Link is a card-backed method saved through Stripe Link.
        email:
          type: string
          format: email
          description: Link account email; present only when type is link.
      allOf:
        - if:
            properties:
              type:
                const: link
            required:
              - type
          then:
            required:
              - email
            properties:
              brand:
                type: "null"
              last4:
                type: "null"
              exp_month:
                type: "null"
              exp_year:
                type: "null"
        - if:
            properties:
              type:
                const: card
            required:
              - type
          then:
            required:
              - brand
              - last4
              - exp_month
              - exp_year
            properties:
              brand:
                type: string
              last4:
                type: string
                pattern: ^[0-9]{4}$
              exp_month:
                type: integer
                minimum: 1
                maximum: 12
              exp_year:
                type: integer
                minimum: 2000
                maximum: 9999
    CreatePaymentMethodSetup:
      type: object
      additionalProperties: false
      required:
        - success_url
        - cancel_url
      properties:
        success_url:
          type: string
          format: uri
          maxLength: 2048
          description: BoxRouter website URL after setup; `{SETUP_ID}` is replaced. Returning here does not prove setup completed.
        cancel_url:
          type: string
          format: uri
          maxLength: 2048
          description: BoxRouter website URL if the payer abandons setup.
    StartedPaymentMethodSetup:
      type: object
      additionalProperties: false
      required:
        - id
        - status
        - checkout_url
      properties:
        id:
          type: string
        status:
          type: string
          enum:
            - pending
            - confirmed
            - canceled
          description: Confirmed only after Stripe reports the setup succeeded.
        checkout_url:
          type: string
          format: uri
          description: Stripe-hosted setup page; no payment is collected.
    CreditSetupIntent:
      type: object
      additionalProperties: false
      required:
        - client_secret
        - publishable_key
      properties:
        client_secret:
          type: string
          minLength: 1
        publishable_key:
          type: string
          minLength: 1
    ReorderCreditPaymentMethods:
      type: object
      additionalProperties: false
      required:
        - payment_method_ids
      properties:
        payment_method_ids:
          type: array
          minItems: 1
          uniqueItems: true
          items:
            type: string
            pattern: ^pm_[A-Za-z0-9_]+$
          description: Complete order of the workspace's saved card and Link payment methods.
    PaymentMethodSetup:
      type: object
      additionalProperties: false
      required:
        - id
        - status
      properties:
        id:
          type: string
        status:
          type: string
          enum:
            - pending
            - confirmed
            - canceled
          description: Confirmed only after Stripe reports the setup succeeded.
    CreditAutoTopUp:
      type: object
      additionalProperties: false
      required:
        - enabled
        - threshold_minor
        - amount_minor
        - email_delivery
      properties:
        enabled:
          type: boolean
        threshold_minor:
          type:
            - integer
            - "null"
          minimum: 0
          description: Start one top-up when available credit falls below this US-cent trigger level; null until configured.
        amount_minor:
          type:
            - integer
            - "null"
          minimum: 1
          description: Top-up in whole US cents, within current purchase_bounds_minor; null until configured.
        last_attempt:
          type: object
          additionalProperties: false
          required:
            - purchase_id
            - status
            - at
          properties:
            purchase_id:
              type: string
            status:
              type: string
              enum:
                - pending
                - confirmed
                - failed
            at:
              type: string
              format: date-time
            fallback_card:
              $ref: "#/components/schemas/MaskedCreditCard"
              description: Set when a payment method after the first in priority order was charged; Link methods include their account email.
          description: Most recent top-up attempt, if one has been made.
        disabled_reason:
          type: string
          enum:
            - payment_declined
            - authentication_required
            - payment_method_required
            - refund_accepted
            - all_cards_failed
          x-boxrouter-open-enum: true
          description: Why auto top-up was disabled after a decline, required authentication, or removal of the last saved card.
        email_delivery:
          type: string
          enum:
            - configured
            - log_only
          description: Whether SMTP and a verified workspace-owner address are configured for auto-top-up failure notices; configured does not guarantee successful delivery.
      allOf:
        - if:
            properties:
              enabled:
                const: true
            required:
              - enabled
          then:
            properties:
              threshold_minor:
                type: integer
                minimum: 0
              amount_minor:
                type: integer
                minimum: 1
            not:
              required:
                - disabled_reason
              properties:
                disabled_reason: true
    UpdateCreditAutoTopUp:
      type: object
      additionalProperties: false
      required:
        - enabled
        - threshold_minor
        - amount_minor
      properties:
        enabled:
          type: boolean
        threshold_minor:
          type: integer
          minimum: 0
          description: Start one top-up when available credit falls below this US-cent trigger level.
        amount_minor:
          type: integer
          minimum: 1
          description: Top-up in whole US cents, within the current purchase_bounds_minor.
    OpenSandboxTerminal:
      type: object
      additionalProperties: false
      properties:
        columns:
          $ref: "#/components/schemas/columns"
        rows:
          $ref: "#/components/schemas/rows"
        timeout_seconds:
          x-boxrouter-input-error: Terminal timeout_seconds must be an integer from 1 to 3600
          type: integer
          minimum: 1
          maximum: 3600
          default: 600
          description: Latest end of the shell. The API worker stays with the terminal for this long.
    columns:
      x-boxrouter-input-error: Terminal columns must be an integer from 1 to 1000
      type: integer
      minimum: 1
      maximum: 1000
      default: 80
    rows:
      x-boxrouter-input-error: Terminal rows must be an integer from 1 to 1000
      type: integer
      minimum: 1
      maximum: 1000
      default: 24
    SandboxTerminal:
      type: object
      required:
        - id
        - sandbox_id
        - state
        - exit_code
      properties:
        id:
          type: string
        sandbox_id:
          type: string
        state:
          type: string
          description: Known states are running, exited and unknown.
        exit_code:
          type:
            - integer
            - "null"
          description: Present only after a confirmed shell exit; 129 follows a hang-up. Null is not success.
    SandboxTerminalEventSSE:
      type: object
      additionalProperties: false
      required:
        - data
      properties:
        id:
          type: string
          description: Hosted replay cursor when supplied; availability and retention follow the operation.
        event:
          type: string
        data:
          $ref: "#/components/schemas/SandboxTerminalEvent"
      description: SSE transport envelope. The JSON payload retains its own type and native fields; EOF is not workload completion.
    SandboxTerminalEvent:
      oneOf:
        - $ref: "#/components/schemas/SandboxTerminalOutputEvent"
        - $ref: "#/components/schemas/SandboxTerminalExitEvent"
        - $ref: "#/components/schemas/SandboxTerminalErrorEvent"
      discriminator:
        propertyName: type
        mapping:
          output: "#/components/schemas/SandboxTerminalOutputEvent"
          exit: "#/components/schemas/SandboxTerminalExitEvent"
          error: "#/components/schemas/SandboxTerminalErrorEvent"
    SandboxTerminalOutputEvent:
      type: object
      required:
        - type
        - sequence
        - data_base64
      properties:
        type:
          type: string
          const: output
        sequence:
          type: integer
          minimum: 0
        data_base64:
          type: string
          contentEncoding: base64
          description: Exact PTY output bytes. Chunk boundaries may change.
    SandboxTerminalExitEvent:
      type: object
      required:
        - type
        - sequence
        - exit_code
      properties:
        type:
          type: string
          const: exit
        sequence:
          type: integer
          minimum: 0
        exit_code:
          type: integer
    SandboxTerminalErrorEvent:
      type: object
      required:
        - type
        - error
      properties:
        type:
          type: string
          const: error
        sequence:
          type: integer
          minimum: 0
        error:
          $ref: "#/components/schemas/error"
    error:
      type: object
      required:
        - code
        - message
        - request_id
        - outcome
        - retry
        - retry_after_seconds
      properties:
        code:
          type: string
          description: |
            Stable machine category. Operation failures use SandboxProviderSpec error codes,
            including invalid_request, invalid_state, not_found, unauthorized,
            incompatible_requirements, no_eligible_route, capacity_unavailable,
            capability_unavailable, contract_violation, resource_limit, timeout and
            provider_error. Hosted service failures add method_not_allowed,
            unauthenticated, forbidden, actions_sandbox_protected,
            insufficient_credit, spend_limit_exceeded,
            provider_not_enabled, credentials_rejected, conflict, rate_limited, provider_quota, request_timeout, overloaded,
            database_unavailable, payment_method_required, payment_declined, promotion_already_redeemed, account_exists, and internal_error. unsupported_feature and
            output_limit are earlier names still returned by some routes.
            Organization and membership operations also use organization_changed, last_admin,
            stale_membership, directory_managed and change_in_progress. Key ownership and
            invitation operations use idempotency_key_reused and conflict.
            Credit refunds and purchase quotes also use refund_window_closed,
            nothing_to_refund, refund_below_minimum, refund_in_progress,
            purchase_in_progress, dispute_open, already_refunded,
            refund_amount_changed, refund_fee_unavailable, quote_changed,
            session_required, refund_daily_limit, refund_rate_limit,
            refund_retry_requires_support, idempotency_key_reused and fraud_review.
            Card setup adds card_setup_rate_limit (429, with Retry-After): a workspace may start five
            card setups an hour.
            A provider that refuses the request for its own plan or quota limit answers
            429 provider_quota with the provider's words in native_message; rate_limited is
            BoxRouter's own limit.
            Routing Prevent Overrides refuses a request containing preferences.order,
            preferences.sort or preferences.allow_fallbacks with routing_overrides_forbidden,
            outcome not_started and details.fields naming the prohibited inputs. No provider
            effect occurs.
            Launch operations also use invalid_request, secret_missing, agent_not_found,
            no_eligible_route and launch_failed.
            A process or terminal is observed by one API task. When that task stops, process
            output, result and stdin answer 410 process_observer_lost (reading or cancelling the
            process still works), and a terminal on a provider that cannot reattach it answers
            410 terminal_observer_lost. invalid_observer_hop (403) refuses an internal hop
            between API tasks that is not signed; clients never send one.
            Opening a preview that the provider answered with other access than the request
            asked for returns 502 preview_access_refused: that preview is never returned, and
            details say whether it was ended.
            Starting a process with wait_for_port answers 409 port_not_listening, outcome partial,
            when the process exits before the port accepts connections or the port stays closed for
            30 seconds; the process is ended and details carry the output so far.
            A request that must store content (a volume, a saved artifact) under transient content
            retention answers 409 privacy_persistence_conflict with outcome not_started.
            A sandbox request that stops before anything is created also answers
            lifetime_cap_exceeded, knob_not_named and unrecognised_word (400), and price_unknown and
            policy_denied (409), each with outcome not_started; the Actions codes job_claim_lost,
            actions_authority_changed, actions_candidate_ceiling_withdrawn and
            actions_candidate_outside_ceiling (409) say the job's claim, settings or admitted routes
            changed; provider_budget_exhausted (409) says a run's provider budget allows no
            further create. A create that names an image Cloud cannot use answers 400
            image_architecture_unsupported, image_digest_resolution_failed,
            image_preparation_unsupported or private_image_managed_route_unavailable, and
            privacy_admission_unavailable when privacy cannot be enforced; each says what to change.
            Launch and source-repository refusals name the repository or manifest fault (code_ref_not_found,
            code_repository_*, source_*, invalid_source_manifest, invalid_manifest_encoding,
            invalid_run_spec, repository_*, reserved_env_refused, launch_run_position_conflict,
            code_reference_moved, dockerfile_context_unsupported, image_tag_unresolved,
            recovery_sandbox_mismatch); the GitHub-side ones (source_rate_limited,
            source_resolution_failed, source_resolution_timeout, code_repository_resolution_failed)
            are safe to resend. image_setup_timeout, managed_funding_unavailable,
            lifetime_funding_conflict, candidate_admission_unavailable, secrets_unavailable and the
            Actions candidate codes (actions_candidate_record_invalid, actions_create_budget_exhausted,
            actions_create_format_mismatch) complete the creation and extension refusals.
            dispatch_limit_reached, inline_secret_values_refused and launch_incomplete come from
            launch runs, provider_policy_unknown from an operation whose privacy evidence cannot be
            verified, and quota_exceeded from a full managed provider account.
            Volume operations also use volume_busy, volume_lease_lost, volume_not_attached,
            volume_incompatible, volume_save_failed and volume_restore_failed. Creating a
            sandbox on a volume whose writer is ending waits for the release (at most 120
            seconds) before it can answer volume_busy.
            A team member's new personal API key is paused until WorkOS membership sync catches
            up: it answers 403 forbidden whose message and fix say the key becomes active once
            the team membership syncs, usually within a minute, and the call can be retried.
            Clients must preserve and handle unknown categories explicitly.
          x-boxrouter-error-codes:
            account_exists:
              - 409
            account_unhealthy:
              - 402
            actions_authority_changed:
              - 409
            actions_candidate_ceiling_withdrawn:
              - 409
            actions_candidate_outside_ceiling:
              - 409
            actions_candidate_record_invalid:
              - 409
            actions_create_budget_exhausted:
              - 409
            actions_create_format_mismatch:
              - 409
            actions_sandbox_protected:
              - 403
            agent_not_found:
              - 404
            already_refunded:
              - 409
            authorization_error:
              - 403
            candidate_admission_unavailable:
              - 503
            capability_unavailable:
              - 409
              - 503
            capability_unverified:
              - 409
            card_setup_rate_limit:
              - 429
            change_in_progress:
              - 409
            code_ref_not_found:
              - 404
            code_reference_moved:
              - 409
            code_repository_not_authorized:
              - 403
            code_repository_not_found:
              - 404
            code_repository_not_private:
              - 400
            code_repository_not_public:
              - 404
            code_repository_resolution_failed:
              - 502
            command_failed:
              - 409
            command_observation_busy:
              - 409
            conflict:
              - 409
            contract_violation:
              - 409
            credentials_rejected:
              - 409
              - 422
            database_unavailable:
              - 503
            directory_managed:
              - 409
            dispatch_limit_reached:
              - 409
            dispute_open:
              - 409
            dockerfile_context_unsupported:
              - 400
            expired:
              - 410
            forbidden:
              - 403
            fraud_review:
              - 409
            gate_unavailable:
              - 409
            idempotency_key_reused:
              - 409
              - 422
            identity_unavailable:
              - 503
            image_architecture_unsupported:
              - 400
            image_digest_resolution_failed:
              - 400
            image_preparation_unsupported:
              - 400
            image_setup_timeout:
              - 504
            image_tag_unresolved:
              - 400
            inline_secret_values_refused:
              - 422
            insufficient_credit:
              - 402
            internal_error:
              - 500
            invalid_manifest_encoding:
              - 400
            invalid_observer_hop:
              - 403
            invalid_request:
              - 400
              - 413
              - 422
              - 428
            invalid_run_spec:
              - 400
            invalid_source_manifest:
              - 400
            invalid_state:
              - 409
            job_claim_lost:
              - 409
            knob_not_named:
              - 400
            last_admin:
              - 409
            launch_failed:
              - 409
            launch_incomplete:
              - 409
            launch_run_position_conflict:
              - 409
            lifetime_cap_exceeded:
              - 400
            lifetime_funding_conflict:
              - 409
            lifetime_limit:
              - 409
            lifetime_used_up:
              - 409
            managed_funding_unavailable:
              - 503
            method_not_allowed:
              - 405
            no_eligible_route:
              - 409
            not_found:
              - 404
            not_named:
              - 409
            not_running:
              - 409
            nothing_to_refund:
              - 409
            organization_changed:
              - 409
            output_limit:
              - 413
            overloaded:
              - 503
            payment_declined:
              - 402
            payment_method_required:
              - 409
            payment_provider_rejected:
              - 502
            payment_provider_unavailable:
              - 503
            payments_unavailable:
              - 503
            policy_denied:
              - 409
            port_not_listening:
              - 409
            preview_access_refused:
              - 502
            price_unknown:
              - 409
            privacy_admission_unavailable:
              - 400
            privacy_persistence_conflict:
              - 409
            private_image_managed_route_unavailable:
              - 400
            process_observer_lost:
              - 410
            promotion_already_redeemed:
              - 409
            promotion_code_invalid:
              - 404
            provider_budget_exhausted:
              - 409
            provider_error:
              - 502
            provider_not_enabled:
              - 409
            provider_policy_unknown:
              - 409
            provider_quota:
              - 429
            purchase_in_progress:
              - 409
            quota_exceeded:
              - 429
            quote_changed:
              - 409
            rate_limited:
              - 429
            recovery_sandbox_mismatch:
              - 409
            refund_amount_changed:
              - 409
            refund_below_minimum:
              - 409
            refund_daily_limit:
              - 429
            refund_fee_unavailable:
              - 409
            refund_in_progress:
              - 409
            refund_rate_limit:
              - 429
            refund_retry_requires_support:
              - 409
            refund_window_closed:
              - 409
            repository_file_unsupported:
              - 400
            repository_too_large:
              - 400
            request_timeout:
              - 408
            reserved_env_refused:
              - 400
              - 422
            resource_limit:
              - 413
              - 507
            routing_overrides_forbidden:
              - 400
            run_limit_exceeded:
              - 429
            sandbox_ended:
              - 410
            secret_missing:
              - 400
            secrets_unavailable:
              - 503
            session_required:
              - 403
            source_file_not_found:
              - 400
            source_file_too_large:
              - 413
            source_rate_limited:
              - 503
            source_ref_not_found:
              - 400
            source_repository_not_accessible:
              - 404
            source_repository_not_found:
              - 400
            source_repository_not_private:
              - 400
            source_repository_not_public:
              - 400
            source_resolution_failed:
              - 502
            source_resolution_timeout:
              - 504
            spend_limit_exceeded:
              - 402
            stale_membership:
              - 409
            terminal_closed:
              - 410
            terminal_observer_lost:
              - 410
            timeout:
              - 504
            unauthenticated:
              - 401
            unavailable:
              - 503
            unrecognised_word:
              - 400
            unsupported_feature:
              - 422
            volume_busy:
              - 409
            volume_incompatible:
              - 409
            volume_lease_lost:
              - 409
            volume_not_attached:
              - 409
            volume_restore_failed:
              - 502
            volume_save_failed:
              - 409
              - 502
            volume_unavailable:
              - 409
            wrong_region:
              - 421
          x-boxrouter-error-retry:
            account_exists: after_fix
            account_unhealthy: after_fix
            actions_authority_changed: after_fix
            actions_candidate_ceiling_withdrawn: after_fix
            actions_candidate_outside_ceiling: after_fix
            actions_candidate_record_invalid: after_fix
            actions_create_budget_exhausted: after_fix
            actions_create_format_mismatch: after_fix
            actions_sandbox_protected: never
            agent_not_found: after_fix
            already_refunded: never
            authorization_error: check_outcome
            candidate_admission_unavailable: never
            capability_unavailable: check_outcome
            capability_unverified: after_fix
            card_setup_rate_limit: safe
            change_in_progress: safe
            code_ref_not_found: after_fix
            code_reference_moved: after_fix
            code_repository_not_authorized: after_fix
            code_repository_not_found: after_fix
            code_repository_not_private: after_fix
            code_repository_not_public: after_fix
            code_repository_resolution_failed: safe
            command_failed: after_fix
            command_observation_busy: safe
            conflict: check_outcome
            contract_violation: check_outcome
            credentials_rejected: after_fix
            database_unavailable: check_outcome
            directory_managed: never
            dispatch_limit_reached: after_fix
            dispute_open: after_fix
            dockerfile_context_unsupported: after_fix
            expired: check_outcome
            forbidden: after_fix
            fraud_review: safe
            gate_unavailable: after_fix
            idempotency_key_reused: after_fix
            identity_unavailable: safe
            image_architecture_unsupported: after_fix
            image_digest_resolution_failed: after_fix
            image_preparation_unsupported: after_fix
            image_setup_timeout: after_fix
            image_tag_unresolved: after_fix
            inline_secret_values_refused: after_fix
            insufficient_credit: after_fix
            internal_error: never
            invalid_manifest_encoding: after_fix
            invalid_observer_hop: after_fix
            invalid_request: after_fix
            invalid_run_spec: after_fix
            invalid_source_manifest: after_fix
            invalid_state: check_outcome
            job_claim_lost: after_fix
            knob_not_named: after_fix
            last_admin: after_fix
            launch_failed: check_outcome
            launch_incomplete: check_outcome
            launch_run_position_conflict: after_fix
            lifetime_cap_exceeded: after_fix
            lifetime_funding_conflict: after_fix
            lifetime_limit: check_outcome
            lifetime_used_up: after_fix
            managed_funding_unavailable: safe
            method_not_allowed: after_fix
            no_eligible_route: after_fix
            not_found: check_outcome
            not_named: after_fix
            not_running: after_fix
            nothing_to_refund: never
            organization_changed: safe
            output_limit: check_outcome
            overloaded: safe
            payment_declined: after_fix
            payment_method_required: after_fix
            payment_provider_rejected: check_outcome
            payment_provider_unavailable: check_outcome
            payments_unavailable: safe
            policy_denied: after_fix
            port_not_listening: after_fix
            preview_access_refused: never
            price_unknown: after_fix
            privacy_admission_unavailable: after_fix
            privacy_persistence_conflict: after_fix
            private_image_managed_route_unavailable: after_fix
            process_observer_lost: check_outcome
            promotion_already_redeemed: never
            promotion_code_invalid: after_fix
            provider_budget_exhausted: never
            provider_error: check_outcome
            provider_not_enabled: after_fix
            provider_policy_unknown: after_fix
            provider_quota: after_fix
            purchase_in_progress: safe
            quota_exceeded: safe
            quote_changed: after_fix
            rate_limited: safe
            recovery_sandbox_mismatch: check_outcome
            refund_amount_changed: after_fix
            refund_below_minimum: never
            refund_daily_limit: safe
            refund_fee_unavailable: safe
            refund_in_progress: safe
            refund_rate_limit: safe
            refund_retry_requires_support: never
            refund_window_closed: never
            repository_file_unsupported: after_fix
            repository_too_large: after_fix
            request_timeout: safe
            reserved_env_refused: after_fix
            resource_limit: check_outcome
            routing_overrides_forbidden: after_fix
            run_limit_exceeded: after_fix
            sandbox_ended: check_outcome
            secret_missing: after_fix
            secrets_unavailable: never
            session_required: never
            source_file_not_found: after_fix
            source_file_too_large: after_fix
            source_rate_limited: safe
            source_ref_not_found: after_fix
            source_repository_not_accessible: after_fix
            source_repository_not_found: after_fix
            source_repository_not_private: after_fix
            source_repository_not_public: after_fix
            source_resolution_failed: safe
            source_resolution_timeout: safe
            spend_limit_exceeded: after_fix
            stale_membership: after_fix
            terminal_closed: after_fix
            terminal_observer_lost: after_fix
            timeout: check_outcome
            unauthenticated: after_fix
            unavailable: safe
            unrecognised_word: after_fix
            unsupported_feature: after_fix
            volume_busy: after_fix
            volume_incompatible: after_fix
            volume_lease_lost: after_fix
            volume_not_attached: after_fix
            volume_restore_failed: check_outcome
            volume_save_failed: check_outcome
            volume_unavailable: after_fix
            wrong_region: after_fix
        message:
          type: string
        details:
          type: object
          description: |
            Code-specific context, spelled for programs. no_eligible_route carries considered,
            excluded, closest and nearest; knob_not_named the knob and its values;
            unrecognised_word the path, the word and the known ones; not_named the gate, what
            the call needed beyond the grant and the corrected requirements line; gate_unavailable
            the gate and account; lifetime_limit the ceiling and this run. A creation that
            stopped before a sandbox existed may also carry `fallbacks_allowed`, `stopped_reason`
            and `attempts`, spelled and bounded as in the sandbox route selection.
            capability_unavailable may identify the needed capability. A launch preparation failure can follow sandbox creation;
            inspect outcome and cleanup details before creating another. Clients must
            preserve fields they do not know.
          properties:
            selection_version:
              type: integer
              minimum: 0
              description: Current selected organization version for organization_changed.
            considered:
              type: integer
              minimum: 0
              description: no_eligible_route only; how many routes were considered.
            excluded:
              type: array
              description: no_eligible_route only; every considered route that cannot serve the request. Each entry names its `provider` and the checks it failed (`failed` for a sandbox request, `reasons` and `exclusions` for a launch); clients must preserve fields they do not know.
              items:
                type: object
                required:
                  - provider
                properties:
                  provider:
                    type: string
            closest:
              type:
                - object
                - "null"
              description: no_eligible_route only; the excluded route that fails fewest checks, or null.
            nearest:
              type: array
              description: no_eligible_route only; the smallest changes that would make a route eligible, nearest first. An entry names the change (`change` and `then_eligible` for a sandbox request, `provider`, `distance` and `failing` for a launch); clients must preserve fields they do not know.
              items:
                type: object
            requirement:
              type: string
              description: The SandboxProviderSpec family whose requirement would make this operation available.
            purchase_id:
              type: string
              description: Retained failed credit purchase ID, when a saved-card charge was declined.
            preview:
              $ref: "#/components/schemas/CreditRefundPreview"
              description: Current refund preview returned with 409 refund_amount_changed.
            quote:
              $ref: "#/components/schemas/CreditPurchaseQuote"
              description: Current purchase quote returned with 409 quote_changed.
            redemption:
              $ref: "#/components/schemas/CreditPromotion"
              description: Original promotion redemption returned with 409 promotion_already_redeemed.
            scope:
              type: string
              enum:
                - workspace
              description: spend_limit_exceeded only; which configured cap refused admission.
            period:
              type: string
              enum:
                - total
                - daily
                - monthly
              description: spend_limit_exceeded only; the cap period that applies.
            limit_nanodollars:
              type: integer
              minimum: 1
              description: spend_limit_exceeded only; exact configured USD nanodollars.
            requested_nanodollars:
              type: integer
              minimum: 1
              description: spend_limit_exceeded only; exact proposed managed hold.
            sign_in_method:
              type: string
              description: "account_exists only: how the existing account with this verified address signs in (google, github, email or other)."
            masked_email:
              type: string
              description: "account_exists only: that account's address, masked as `t…@gmail.com`."
            failure_reason:
              type: string
              maxLength: 100
              description: Short safe card-decline reason; raw Stripe messages are not returned.
            names:
              type: array
              uniqueItems: true
              items:
                $ref: "#/components/schemas/SecretName"
              description: All manifest secret names that could not be resolved.
            fields:
              type: array
              uniqueItems: true
              items:
                type: string
                enum:
                  - preferences.order
                  - preferences.sort
                  - preferences.allow_fallbacks
              description: routing_overrides_forbidden only; the prohibited request inputs.
            volume_id:
              type: string
              description: BoxRouter Volume ID involved in the error.
            writer_sandbox_id:
              type: string
              description: volume_busy only; the sandbox that holds the volume's writer lease, when it is a recorded sandbox.
            checkpoint:
              type: integer
              minimum: 1
              description: Volume checkpoint involved in the error.
            limit_bytes:
              type: integer
              minimum: 0
            observed_bytes:
              type: integer
              minimum: 0
            mismatch:
              type: object
              additionalProperties: false
              required:
                - field
                - recorded
                - requested
              properties:
                field:
                  type: string
                  enum:
                    - image
                    - arch
                recorded:
                  type: string
                requested:
                  type: string
            volume_phase:
              type: string
              enum:
                - check
                - branch
                - lease
                - identity
                - install
                - credential
                - mount
                - metadata
                - checkpoint
                - commit
                - write
            phase:
              type: string
              enum:
                - code
                - setup
                - files
                - background
                - previews
              description: Preparation phase that failed.
            command:
              type: string
              description: Command whose nonzero exit stopped launch preparation.
            exit_code:
              type: integer
              description: Exit code of the failed command, or of the process that exited before its port listened.
            stderr_tail:
              type: string
              description: |
                At most the final 4 KiB of stderr from the failed command, with resolved secret values
                redacted. port_not_listening carries the same tail of the process's stderr.
            stdout_tail:
              type: string
              description: port_not_listening only; at most the final 4 KiB of the process's stdout.
            agent:
              type: string
              description: Unknown catalog agent name.
            port:
              type: integer
              minimum: 1
              maximum: 65535
              description: The sandbox port whose preview was refused (preview_access_refused) or that never accepted connections (port_not_listening).
            revoked:
              type: boolean
              description: |
                preview_access_refused only; true when the unwanted preview was ended and no
                access to the port remains. False: it may still be reachable (outcome unknown).
            preview_id:
              type: string
              description: |
                preview_access_refused only; the identifier the provider gave the preview that may
                still be reachable. Present only for a public preview.
            url:
              type: string
              format: uri
              description: |
                preview_access_refused only; the address of a public preview that may still be
                reachable. Never present for a private preview, whose address holds a credential,
                and never when it was ended.
        request_id:
          type: string
        doc_url:
          type:
            - string
            - "null"
          format: uri
          description: "Where the documentation explains this code: `https://boxrouter.ai/docs/error-codes#` followed by the code, for example `https://boxrouter.ai/docs/error-codes#no_eligible_route`. The page has one anchored row per code with what it means and what to do. BoxRouter sends it with every error; it is null for a code that has no documented row. Clients must accept an error body that omits it."
        fix:
          type:
            - string
            - "null"
          minLength: 1
          description: Actionable change for the caller when the request cannot be completed. A refusal (4xx) always carries one; a server or provider fault, including an error event that ends a terminal or process stream, can answer null.
        operation_id:
          type:
            - string
            - "null"
          description: Durable server operation ID for reconciliation after uncertain mutations.
        outcome:
          type: string
          enum:
            - not_started
            - completed
            - partial
            - unknown
          description: |
            What is known about the requested side effect. `not_started` means nothing ran
            anywhere, at BoxRouter or at a provider; a launch-setup or process-start failure
            after dispatch is `partial` or `unknown`. Unknown and partial forbid automatic
            replay and always carry `retry: check_outcome`. An HTTP status alone does not establish
            outcome.
            Many refusals report not_started before the provider is asked anything. A read
            (GET or HEAD) has no side effect to be unsure about, so its failures report
            not_started and carry the code's own retry value. A launch preparation failure
            happens after the sandbox was created and setup commands may already have run, and
            deleting the sandbox does not undo effects outside it: it reports partial once the
            cleanup is confirmed and unknown if it is not. A process that wait_for_port ended
            reports partial. volume_restore_failed on a resume or create,
            and volume_save_failed after the guest was read, report unknown or partial; they
            carry the operation_id when the call was recorded, and their fix does not offer to
            send the call again unchanged.
        retry:
          type: string
          enum:
            - safe
            - after_fix
            - check_outcome
            - never
          x-boxrouter-open-enum: true
          description: |
            What the caller may do next, fixed per code (x-boxrouter-error-retry): safe resends the
            same request, after_fix changes the request or setup first, check_outcome reads
            `outcome` and the resource before resending, never does not resend. Branch on this
            instead of the HTTP status.
        retry_after_seconds:
          type:
            - integer
            - "null"
          minimum: 0
          description: "How long to wait before a `retry: safe` resend, as the Retry-After header says it; null when there is no wait to name."
        provider:
          type:
            - string
            - "null"
        native_code:
          type:
            - string
            - "null"
          description: The provider's machine-readable error code, unchanged, when it reports one.
        native_status:
          type:
            - integer
            - "null"
          description: The provider's HTTP status, unchanged, when the failure came from a provider response.
        native_message:
          type:
            - string
            - "null"
          maxLength: 4096
          description: The provider's own error message when it answered with an HTTP 4xx refusal (for example a plan limit); null otherwise. Capped at 512 characters, with whitespace collapsed. Credential secrets and token-shaped strings are removed before return. The literal `[redacted]` marker is used when all credential secret fields are at least 12 characters; legacy saved secrets shorter than 12 characters are removed without the marker.
    SandboxTerminalInput:
      type: object
      additionalProperties: false
      required:
        - data_base64
      properties:
        data_base64:
          type: string
          contentEncoding: base64
          maxLength: 87384
          description: Canonical base64 of at most 65536 exact bytes. No newline is appended.
    SandboxTerminalSize:
      type: object
      additionalProperties: false
      properties:
        columns:
          x-boxrouter-input-error: Terminal columns must be an integer from 1 to 1000
          type: integer
          minimum: 1
          maximum: 1000
          default: 80
        rows:
          x-boxrouter-input-error: Terminal rows must be an integer from 1 to 1000
          type: integer
          minimum: 1
          maximum: 1000
          default: 24
    SecretList:
      type: object
      additionalProperties: false
      required:
        - secrets
      properties:
        secrets:
          type: array
          items:
            $ref: "#/components/schemas/Secret"
    Secret:
      type: object
      additionalProperties: false
      required:
        - name
        - created_at
        - updated_at
      properties:
        name:
          $ref: "#/components/schemas/SecretName"
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    PutSecret:
      type: object
      additionalProperties: false
      required:
        - value
      properties:
        value:
          type: string
          maxLength: 16384
          pattern: ^[^\u0000]*$
          writeOnly: true
          description: Secret value. It is never returned, logged or included in an error response.
    PreviewPage:
      type: object
      required:
        - data
        - next_cursor
      description: The previews one sandbox has open, recorded by BoxRouter and read without observing any provider.
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/Preview"
        next_cursor:
          type:
            - string
            - "null"
    Preview:
      type: object
      required:
        - id
        - port
        - url
        - access
        - credential
        - headers
        - expires_at
        - created_at
      description: |
        One open preview, as BoxRouter recorded it. A resume forgets a sandbox's previews,
        because no adapter can re-read one: open again rather than caching a URL.
      properties:
        id:
          type: string
          description: BoxRouter's own ID, prefixed pv_, which a revoke names.
        port:
          type: integer
          minimum: 1
          maximum: 65535
        url:
          type: string
          format: uri
        access:
          type: string
          enum:
            - public
            - private
        credential:
          type:
            - string
            - "null"
          enum:
            - link
            - header
            - null
          description: |
            Where a private preview's credential is: link, in the url, which a person can
            click; header, in headers, to send with every request. Null for a public one.
        headers:
          type: object
          additionalProperties:
            type: string
          description: Credential headers to send with every request; empty unless credential is header.
        expires_at:
          type:
            - string
            - "null"
          format: date-time
          description: |
            Set only where the provider enforces an expiry, and never sooner than a granted
            link_lifetime_seconds after created_at. There is no way to shorten a link (D30).
        created_at:
          type: string
          format: date-time
    OpenPreview:
      type: object
      additionalProperties: false
      required:
        - port
      description: |
        A preview request for one port declared by the sandbox's previews requirement.
      properties:
        port:
          type: integer
          minimum: 1
          maximum: 65535
          description: Must be one of the gate's ports, else not_named.
        access:
          type: string
          enum:
            - public
            - private
          description: |
            Which kind of link to open. Required only when granted.access holds both values,
            and not allowed when the request named "any": then the result says which it is.
        credential:
          type: string
          enum:
            - link
            - header
          description: |
            Where a private preview carries its credential, and only with access private.
            Left out, the adapter picks and the result says which.
    Browser:
      type: object
      required:
        - automation
        - version
        - executable
      properties:
        automation:
          type: array
          minItems: 1
          items:
            type: string
            enum:
              - playwright
              - cdp
          description: Channels that loaded a page served inside the guest.
        version:
          type: string
        executable:
          type: string
    Paused:
      type: object
      required:
        - state
        - keeps
        - outside_grant
        - lifetime
      properties:
        state:
          type: string
          enum:
            - paused
        keeps:
          type: string
          enum:
            - files_only
            - files_and_memory
        outside_grant:
          type: array
          items:
            type: string
            enum:
              - keeps
          description: Observed values that fell outside the pause_resume grant.
        lifetime:
          $ref: "#/components/schemas/Lifetime"
        pause_expires_at:
          type:
            - string
            - "null"
          format: date-time
          description: Deadline after which a managed paused sandbox is terminated; null when no managed pause deadline applies.
    Lifetime:
      type: object
      required:
        - seconds
        - remaining_seconds
        - ends_at
        - kept_until
        - at_end
        - max_seconds
        - extendable
      description: |
        How long the sandbox may live. It counts from the provider's create, as with the
        provider's own SDK (BoxRouter's clock starts when it sends the create). Whether paused
        time counts depends on the provider.
      properties:
        seconds:
          type:
            - integer
            - "null"
          minimum: 0
          description: |
            The lifetime in seconds. 0 only for a sandbox that has no lifetime record (one that
            ended before lifetimes were recorded). Null for a sandbox created with no lifetime
            (`lifetime_seconds: null`), which runs until it is destroyed.
        remaining_seconds:
          type:
            - integer
            - "null"
          minimum: 0
          description: |
            As of observed_at. Equal to seconds while creating, and frozen while the sandbox is not
            running. Null for a sandbox with no lifetime.
        ends_at:
          type:
            - string
            - "null"
          format: date-time
          description: |
            Set only while running. The end never comes before it; how soon after is not
            promised. Null while creating, paused or ended, and for a sandbox with no lifetime.
        kept_until:
          type:
            - string
            - "null"
          format: date-time
          description: |
            While paused: when the sandbox is destroyed if it is not resumed, 30
            days after it last ran, sooner where the provider keeps it for less. Where the lifetime
            keeps counting while paused, the sandbox ends when the lifetime runs out, so
            kept_until is that moment when it falls inside the 30 days; extending the lifetime
            moves it. Where the clock stops while paused, the full 30 days apply.
        at_end:
          type:
            - string
            - "null"
          enum:
            - destroy
            - pause
            - null
          description: What happens when the lifetime is used up. Null for a sandbox with no lifetime.
        max_seconds:
          description: |
            The route's longest single run, from one start to the next pause or end, as
            narrowed for this account. A declared number: an integer, "unlimited", "unknown"
            or a floor.
          oneOf:
            - type: integer
              minimum: 1
            - type: string
              enum:
                - unlimited
                - unknown
            - type: object
              required:
                - at_least
              additionalProperties: false
              properties:
                at_least:
                  type: integer
                  minimum: 1
        extendable:
          type: boolean
    Resume:
      type: object
      additionalProperties: false
    Resumed:
      type: object
      required:
        - state
        - cold_restart
        - outside_grant
        - lifetime
      properties:
        state:
          type: string
          enum:
            - running
        cold_restart:
          type: boolean
          description: Whether running processes were lost while the sandbox was paused or stopped.
        outside_grant:
          type: array
          items:
            type: string
            enum:
              - cold_restart
          description: Observed values that fell outside the pause_resume grant.
        lifetime:
          $ref: "#/components/schemas/Lifetime"
    UpdateNetworkPolicy:
      type: object
      additionalProperties: false
      required:
        - policy
      description: |
        The outbound policy to enforce from now on, with its entries. An allowlist needs at
        least one allow entry and may carry deny entries, which win over its allow entries; a
        denylist needs at least one deny entry and takes no allow entries; deny_all and open
        take none.
      properties:
        policy:
          type: string
          enum:
            - deny_all
            - allowlist
            - denylist
            - open
        allow:
          type: array
          maxItems: 4096
          items:
            $ref: "#/components/schemas/NetworkEntry"
        deny:
          type: array
          maxItems: 4096
          items:
            $ref: "#/components/schemas/NetworkEntry"
      allOf:
        - if:
            properties:
              policy:
                const: allowlist
            required:
              - policy
          then:
            required:
              - allow
        - if:
            properties:
              policy:
                const: denylist
            required:
              - policy
          then:
            required:
              - deny
            not:
              required:
                - allow
        - if:
            properties:
              policy:
                enum:
                  - deny_all
                  - open
            required:
              - policy
          then:
            not:
              anyOf:
                - required:
                    - allow
                - required:
                    - deny
    NetworkEntry:
      description: |
        A host name or address, or an HTTP rule object. A rule object needs the sandbox's
        network_lockdown request to have named http_rules true.
      oneOf:
        - type: string
          minLength: 1
        - $ref: "#/components/schemas/NetworkHttpRule"
    NetworkHttpRule:
      type: object
      additionalProperties: false
      required:
        - hosts
      dependentRequired:
        paths:
          - methods
      description: |
        Requests to these hosts, optionally only these methods and paths. A matching allowlist
        request succeeds and other traffic to its hosts is refused; a matching denylist request is
        refused and other traffic to its hosts succeeds.
      properties:
        hosts:
          type: array
          minItems: 1
          uniqueItems: true
          items:
            type: string
            minLength: 1
          description: Host names, each once. An allowlist rule with methods names hosts without `*.`.
        methods:
          type: array
          minItems: 1
          uniqueItems: true
          items:
            type: string
            pattern: ^[!#$%&'*+.^_`|~0-9A-Z-]+$
          description: Upper-case HTTP methods, each once, or the single entry `ANY` for every method.
        paths:
          type: array
          minItems: 1
          uniqueItems: true
          items:
            type: string
            pattern: ^/
          description: |
            Path globs starting with `/`; needs methods. `*` matches any run except `/`, `**` any
            run including `/`; the whole path without its query string must match.
    NetworkPolicyUpdated:
      type: object
      required:
        - policy
      properties:
        policy:
          type: string
          enum:
            - deny_all
            - allowlist
            - denylist
            - open
          description: The policy now enforced.
    ConnectProviderAccount:
      type: object
      additionalProperties: false
      required:
        - name
        - credentials
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 100
          pattern: ^[^\u0000]*$
        credentials:
          $ref: "#/components/schemas/Credentials"
    Credentials:
      description: |
        One provider's account credentials as flat fields. `provider` names an available
        provider (`available_providers` of listProviderAccounts); every other property is one of
        that provider's `credential_fields`. A required field is a nonempty string; an optional
        field is a string or null, and null is the same as leaving it out. Secret values contain
        12 through 16384 characters and identifiers 1 through 256. A field the provider does not
        declare, a missing or null required field, or a value of another type is rejected; errors
        name the field, never its value. Accepted only on credential-management endpoints, over
        TLS. Write-only and encrypted at rest. Never logged or returned.
      type: object
      required:
        - provider
      maxProperties: 9
      propertyNames:
        pattern: ^[a-z][a-z0-9_]{0,63}$
      properties:
        provider:
          type: string
          minLength: 1
          maxLength: 64
      additionalProperties:
        type:
          - string
          - "null"
        minLength: 1
        maxLength: 16384
        writeOnly: true
    ProviderAccount:
      type: object
      required:
        - id
        - name
        - provider
        - status
        - credential_version
        - settings
      properties:
        id:
          type: string
        name:
          type: string
        provider:
          type: string
        status:
          type: string
          description: |
            Known states are pending, enabled, invalid_credentials, credentials_rejected and unavailable.
            Pending is a legacy state for an account saved before provider approval. Enabled means
            hosted BYOK is approved and the provider did not reject the credentials when they were saved.
            invalid_credentials and unavailable remain readable for older records. credentials_rejected
            means a later provider call rejected the saved access; credential_failure gives a safe cause
            and the action needed to restore it.
        settings:
          description: Non-secret credential fields saved with the credentials, named as in credential_fields. Legacy accounts may return an empty object. Secrets are never returned; credential_hints may return the last four characters and credential_prefixes only a declared public prefix.
          type: object
          additionalProperties:
            type: string
        credential_version:
          type: integer
          minimum: 1
          description: Monotonic version. Contains no key material or key fingerprint; credential_hints and credential_prefixes may return only the last four characters and a declared public prefix, respectively.
        credential_hints:
          type: object
          description: Last four characters of saved secret credential fields that contain at least 12 characters. No other key material is returned.
          additionalProperties:
            type: string
            pattern: ^.{4}$
        credential_prefixes:
          type: object
          description: Declared public prefixes for saved secret credential fields, returned only when the saved value starts with the declared prefix and at least eight characters remain hidden before the last four. No other key material is returned.
          additionalProperties:
            type: string
            pattern: ^[a-z0-9]{2,8}[_-]$
        credential_failure:
          oneOf:
            - $ref: "#/components/schemas/ProviderCredentialFailure"
            - type: "null"
          description: Present when a later provider call rejected the saved credentials or access.
        standard_image:
          $ref: "#/components/schemas/ProviderStandardImage"
        disconnect:
          oneOf:
            - $ref: "#/components/schemas/ProviderAccountDisconnect"
            - type: "null"
          description: A removal accepted with `wait=false` that is still running or was refused; null otherwise.
        warnings:
          type: array
          description: |
            Notices about this connect. Present only on the response that created the account, and
            only when there is something to say.
          items:
            $ref: "#/components/schemas/ProviderAccountWarning"
    ProviderCredentialFailure:
      type: object
      additionalProperties: false
      required:
        - state
        - status
        - cause
        - remedy
        - detected_at
      properties:
        state:
          type: string
          const: credentials_rejected
        status:
          type: integer
          enum:
            - 401
            - 403
          description: Provider HTTP status returned by the rejected call.
        cause:
          type: string
          enum:
            - provider_rejected_credentials
            - provider_access_denied
          x-boxrouter-open-enum: true
          description: Safe normalized cause; provider response bodies and credential values are excluded.
        remedy:
          type: string
          minLength: 1
          description: The action needed to restore access, based on the provider and HTTP status.
        detected_at:
          type: string
          format: date-time
    ProviderStandardImage:
      type: object
      additionalProperties: false
      required:
        - state
        - region
        - image_version
        - image_digest
        - reason
        - fix
        - checked_at
      oneOf:
        - properties:
            state:
              const: ready
            reason:
              type: "null"
            fix:
              type: "null"
          required:
            - state
            - reason
            - fix
        - properties:
            state:
              const: preparing
            reason:
              type: "null"
            fix:
              type: "null"
          required:
            - state
            - reason
            - fix
        - properties:
            state:
              const: failed
            reason:
              type: string
              minLength: 1
            fix:
              type: string
              minLength: 1
          required:
            - state
            - reason
            - fix
        - properties:
            state:
              const: unavailable
            fix:
              type: string
              minLength: 1
          required:
            - state
      properties:
        state:
          type: string
          enum:
            - ready
            - preparing
            - failed
            - unavailable
        region:
          type: string
        image_version:
          type: string
        image_digest:
          type: string
        reason:
          type:
            - string
            - "null"
          description: Provider's reason when setup failed, or the reason this account cannot use the standard image.
        fix:
          type:
            - string
            - "null"
          description: The one action needed to establish readiness or recover from a failed setup.
        checked_at:
          type:
            - string
            - "null"
          format: date-time
    ProviderAccountDisconnect:
      type: object
      additionalProperties: false
      required:
        - state
        - started_at
        - finished_at
        - error
      description: |
        Progress of a removal accepted with `wait=false`. A finished removal leaves no account
        (404), so there is no completed state. A removal still disconnecting ten minutes after
        it started was stopped with its server and reads as failed; remove the account again.
      properties:
        state:
          type: string
          enum:
            - disconnecting
            - failed
          x-boxrouter-open-enum: true
        started_at:
          type: string
          format: date-time
        finished_at:
          oneOf:
            - type: string
              format: date-time
            - type: "null"
        error:
          description: Present when state is failed. The same code, message and fix the synchronous request returns as its refusal.
          oneOf:
            - $ref: "#/components/schemas/ProviderAccountDisconnectError"
            - type: "null"
    ProviderAccountDisconnectError:
      type: object
      required:
        - code
        - message
        - fix
        - request_id
        - details
      properties:
        code:
          type: string
        message:
          type: string
        fix:
          type:
            - string
            - "null"
        request_id:
          type:
            - string
            - "null"
          description: The removal request's ID.
        details:
          type: object
          description: Code-specific context, such as `sandbox_ids` naming the live sandboxes in the way.
    ProviderAccountWarning:
      type: object
      required:
        - code
        - message
      properties:
        code:
          type: string
          enum:
            - duplicate_credential
          x-boxrouter-open-enum: true
          description: |
            duplicate_credential: another account of this workspace has the same provider, settings
            and last four characters of every secret. New values may appear.
        message:
          type: string
          description: What was noticed and what to do about it.
        account_id:
          type: string
          description: The other account the notice is about.
    ProviderAccountPage:
      type: object
      required:
        - data
        - next_cursor
        - available_providers
        - credential_fields
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/ProviderAccount"
        next_cursor:
          type:
            - string
            - "null"
        available_providers:
          description: Providers this deployment currently enables for saving, testing and using accounts. Independent of this page's account rows.
          type: array
          uniqueItems: true
          items:
            type: string
        provider_metadata:
          description: Public, static presentation metadata keyed by provider ID. This does not indicate whether the provider is approved; use available_providers for that.
          type: object
          additionalProperties:
            type: object
            additionalProperties: false
            required:
              - display_name
            properties:
              display_name:
                type: string
              credential_help_url:
                type: string
        credential_fields:
          description: The credential fields each available provider's connect, replace and test requests take, in the order the provider declares them.
          type: object
          additionalProperties:
            type: array
            items:
              $ref: "#/components/schemas/CredentialField"
    CredentialField:
      type: object
      additionalProperties: false
      required:
        - name
        - secret
        - required
      properties:
        name:
          type: string
          pattern: ^[a-z][a-z0-9_]{0,63}$
        label:
          type: string
          description: Public display label for this credential field.
        placeholder:
          type: string
          description: Static input hint; never a credential value or default.
        secret:
          type: boolean
          description: A secret is write-only; a saved one shows only in credential_hints and credential_prefixes.
        required:
          type: boolean
    TestProviderCredentials:
      description: |
        Supplied flat credential fields. No customer code runs, so a retried Test is safe
        and makes the same read-only provider request.
      type: object
      additionalProperties: false
      required:
        - credentials
      properties:
        credentials:
          $ref: "#/components/schemas/Credentials"
    ProviderAccountTest:
      type: object
      additionalProperties: false
      required:
        - ok
        - native_status
        - native_body
      properties:
        ok:
          type: boolean
        native_status:
          type:
            - integer
            - "null"
          description: Provider's native HTTP status; null when the protocol has no HTTP status.
        native_body:
          type: string
          maxLength: 4096
          description: Provider's response body, truncated to 4096 characters. Exact credential secret values are removed before the body is returned; partial or transformed values can remain. Review the response before sharing it. The literal `[redacted]` marker is used when all credential secret fields are at least 12 characters; legacy saved secrets shorter than 12 characters are removed without the marker.
    ReplaceCredentials:
      type: object
      additionalProperties: false
      required:
        - expected_version
        - credentials
      properties:
        expected_version:
          type: integer
          minimum: 1
        credentials:
          $ref: "#/components/schemas/Credentials"
    VolumePage:
      type: object
      required:
        - data
        - next_cursor
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/Volume"
        next_cursor:
          type:
            - string
            - "null"
        stored_bytes:
          type: integer
          minimum: 0
          description: Current sum of stored bytes for ready retained checkpoints plus last observed disk usage for visible volumes. Not a billing amount.
        live_volume_policy:
          $ref: "#/components/schemas/LiveVolumePolicy"
          description: Present only when live storage is enabled and this workspace is allowlisted. Absence does not authorize live creation.
        allowance_bytes:
          type: integer
          minimum: 0
          description: The volume storage this workspace has included, in bytes. It is workspace configuration, so read it here instead of assuming a number. Storage within it is not charged.
        gib_hours_this_month:
          type: number
          minimum: 0
          description: GiB-hours of stored volume data over the completed UTC hours since the start of this month. Metered usage, not a billing amount.
    Volume:
      type: object
      additionalProperties: false
      required:
        - id
        - name
        - state
        - labels
        - created_at
        - updated_at
        - last_used_at
        - head_checkpoint
        - checkpoint_count
        - stored_bytes
        - limits
        - attachments
      description: |
        A durable folder stored by BoxRouter. Checkpoint and branch operations depend on current
        capability admission. Its backend is internal.
      properties:
        id:
          $ref: "#/components/schemas/VolumeId"
        name:
          $ref: "#/components/schemas/VolumeName"
        state:
          type: string
          enum:
            - ready
            - deleting
          x-boxrouter-open-enum: true
          description: A deleting volume remains visible in GET and list results until remote removal is confirmed; after deletion GET returns 404 and the volume is omitted from list results.
        labels:
          type: object
          additionalProperties:
            type: string
        observed_volume_bytes:
          type:
            - integer
            - "null"
          minimum: 0
          description: Last observed whole-volume disk usage, or null when unavailable. Not a checkpoint size or a billing amount.
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        last_used_at:
          type:
            - string
            - "null"
          format: date-time
          description: Last attach or save; null before the first.
        head_checkpoint:
          type:
            - integer
            - "null"
          minimum: 1
          description: The checkpoint a new attachment restores by default; null while the volume is empty.
        checkpoint_count:
          type: integer
          minimum: 0
        stored_bytes:
          type: integer
          minimum: 0
          description: Total stored bytes for this volume, or last observed whole-volume disk usage (zero before an observation). Prefer observed_volume_bytes, which distinguishes unknown from zero. Not a per-checkpoint size.
        limits:
          $ref: "#/components/schemas/VolumeLimits"
        attachments:
          type: array
          items:
            $ref: "#/components/schemas/VolumeAttachmentSummary"
          description: All attachments, ordered by sandbox_id. Empty when unused.
    VolumeName:
      type: string
      minLength: 1
      maxLength: 63
      pattern: ^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$
    VolumeLimits:
      type: object
      additionalProperties: false
      required:
        - max_checkpoints
      properties:
        max_checkpoints:
          type: integer
          minimum: 1
          description: Maximum retained checkpoints; saving beyond it removes the oldest automatic (pause or destroy) checkpoint first, then the oldest saved one; the head is never removed.
    VolumeAttachmentSummary:
      type: object
      additionalProperties: false
      required:
        - sandbox_id
        - access
        - attached_at
        - restored_checkpoint
      properties:
        sandbox_id:
          type: string
        access:
          type: string
          enum:
            - write
            - read
            - shared
            - branch
          description: Access mode recorded for this attachment.
        attached_at:
          type:
            - string
            - "null"
          format: date-time
        restored_checkpoint:
          type:
            - integer
            - "null"
          minimum: 1
    LiveVolumePolicy:
      type: object
      additionalProperties: false
      required:
        - enabled
        - soft_quota_bytes
      properties:
        enabled:
          type: boolean
          description: Whether the deployment currently permits this allowlisted workspace to create live volumes. Route eligibility is checked separately at attachment.
        soft_quota_bytes:
          type: integer
          minimum: 1
          description: The most volume storage this workspace may hold, in bytes. Attach and save are refused with resource_limit (413) when observed usage is above it; writes already in progress are not stopped. It can be larger than allowance_bytes.
    CreateVolume:
      type: object
      additionalProperties: false
      required:
        - name
      properties:
        name:
          $ref: "#/components/schemas/VolumeName"
          description: Unique among the workspace's volumes that are not deleted.
        labels:
          type: object
          maxProperties: 64
          propertyNames:
            maxLength: 63
          additionalProperties:
            type: string
            maxLength: 255
    VolumeCheckpointPage:
      type: object
      required:
        - data
        - next_cursor
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/VolumeCheckpoint"
        next_cursor:
          type:
            - string
            - "null"
    VolumeCheckpoint:
      type: object
      additionalProperties: false
      required:
        - volume_id
        - checkpoint
        - state
        - created_at
        - reason
        - saved_on
        - image
        - arch
        - user
        - path
        - observed_volume_bytes
        - duration_ms
        - message
      description: |
        An immutable saved filesystem checkpoint identified by (volume_id, checkpoint). Observed
        volume bytes are whole-disk measurements taken before the save, not per-checkpoint sizes.
        Deleting or expiring its API reference does not reclaim backend disk bytes; those remain
        until the volume is erased.
      properties:
        volume_id:
          $ref: "#/components/schemas/VolumeId"
        checkpoint:
          type: integer
          minimum: 1
        state:
          type: string
          enum:
            - ready
          x-boxrouter-open-enum: true
          description: Only committed checkpoints are listed; a save in progress is not a checkpoint.
        created_at:
          type: string
          format: date-time
        reason:
          type: string
          enum:
            - requested
            - destroy
            - pause
          x-boxrouter-open-enum: true
          description: requested is a save call; destroy and pause are saves BoxRouter made first.
        saved_on:
          $ref: "#/components/schemas/VolumeSavedOn"
        image:
          $ref: "#/components/schemas/VolumeImage"
        arch:
          type: string
          enum:
            - amd64
            - arm64
        user:
          $ref: "#/components/schemas/VolumeUser"
        path:
          type: string
          pattern: ^/[^\u0000]*$
        observed_volume_bytes:
          type:
            - integer
            - "null"
          minimum: 0
          description: Whole-volume disk usage observed before this checkpoint was created. May include other retained checkpoints and concurrent writes; not an exact checkpoint size or billing amount.
        duration_ms:
          type: integer
          minimum: 0
          description: Save wall time measured by BoxRouter.
        message:
          type:
            - string
            - "null"
          maxLength: 500
    VolumeSavedOn:
      type: object
      additionalProperties: false
      required:
        - sandbox_id
        - provider
        - class
        - region
      properties:
        sandbox_id:
          type: string
        provider:
          type: string
        class:
          type:
            - string
            - "null"
        region:
          type:
            - string
            - "null"
    VolumeImage:
      type: object
      additionalProperties: false
      required:
        - value
        - digest
      properties:
        value:
          type: string
          description: |
            standard, the exact OCI reference the source sandbox was created with, or
            `prepared:sha256:<source>` for a provider-native artifact: the canonical SHA-256
            identity of the resolved preparation source, so two account-native artifacts
            prepared from the same source carry one volume identity across providers.
        digest:
          type:
            - string
            - "null"
          description: |
            The source OCI digest where BoxRouter knows it (always for standard). Null for a
            prepared artifact: its source key is not an OCI digest.
    VolumeUser:
      type: object
      additionalProperties: false
      required:
        - name
        - uid
      properties:
        name:
          type: string
        uid:
          type: integer
          minimum: 0
    SaveVolume:
      type: object
      additionalProperties: false
      properties:
        sandbox_id:
          type: string
          description: |
            The sandbox expected to hold the writer lease. If another sandbox holds it, or nobody
            does, the save is refused with volume_lease_lost. Omitted means the current holder.
            A mounted branch sandbox may also be named: the save is a ready checkpoint of that
            branch's files. It does not advance the volume head or change the source volume, its
            branch or its lease, so the next create without a checkpoint still restores the head.
            Select the saved checkpoint number to reopen the branch's files. Read-only sandboxes
            cannot save, and a branch that is no longer mounted is refused with volume_lease_lost.
        message:
          type: string
          maxLength: 500
    CreateVolumeUpload:
      type: object
      additionalProperties: false
      required:
        - path
        - size
      properties:
        path:
          $ref: "#/components/schemas/VolumeFilePath"
        size:
          type: integer
          minimum: 0
          maximum: 5497558138880
          description: File size in bytes, from zero through 5 TiB. Even an empty file uses one multipart part.
    VolumeFilePath:
      type: string
      minLength: 1
      maxLength: 1024
      pattern: ^(?!/)(?!.*//)(?!\.{1,2}(?:/|$))(?!.*\/\.{1,2}(?:/|$))(?!.*\u0000).+$
      description: |
        Relative path from the volume root, visible in sandboxes at `/workspace/<path>`.
        Empty paths, a leading slash, empty, `.` or `..` segments, NUL, and paths longer
        than 1024 UTF-8 bytes are refused with `invalid_request` (400).
    VolumeUpload:
      type: object
      additionalProperties: false
      required:
        - id
        - volume_id
        - path
        - size
        - part_size
        - parts
        - expires_at
      description: |
        Multipart upload session. Upload each part URL with an HTTP PUT containing exactly
        the part bytes, then pass each response ETag header to complete. Part URLs and this
        session expire together, six hours after creation plus about one hour for each 70 GiB of the
        declared size, at most 72 hours. The file appears at `/workspace/<path>` in
        sandboxes after completion. An open upload blocks write sandboxes; write attachments
        block creating uploads. Read and shared sandboxes may need to refresh their volume
        cache to see the new file.
      properties:
        id:
          $ref: "#/components/schemas/UploadId"
        volume_id:
          $ref: "#/components/schemas/VolumeId"
        path:
          $ref: "#/components/schemas/VolumeFilePath"
        size:
          type: integer
          minimum: 0
          maximum: 5497558138880
        part_size:
          type: integer
          minimum: 1
        parts:
          type: array
          minItems: 1
          maxItems: 10000
          items:
            $ref: "#/components/schemas/VolumeUploadPart"
        expires_at:
          type: string
          format: date-time
    UploadId:
      type: string
      pattern: ^vup_[0-9a-f]{32}$
    VolumeUploadPart:
      type: object
      additionalProperties: false
      required:
        - number
        - size
        - url
      properties:
        number:
          type: integer
          minimum: 1
          maximum: 10000
        size:
          type: integer
          minimum: 0
        url:
          type: string
          format: uri
          description: Temporary URL to PUT exactly this part's bytes. The response ETag header is required to complete the upload.
    CompleteVolumeUpload:
      type: object
      additionalProperties: false
      required:
        - parts
      properties:
        parts:
          type: array
          minItems: 1
          maxItems: 10000
          items:
            type: object
            additionalProperties: false
            required:
              - number
              - etag
            properties:
              number:
                type: integer
                minimum: 1
                maximum: 10000
              etag:
                type: string
                minLength: 1
    CompletedVolumeUpload:
      type: object
      additionalProperties: false
      required:
        - path
        - size
      properties:
        path:
          $ref: "#/components/schemas/VolumeFilePath"
        size:
          type: integer
          minimum: 0
          maximum: 5497558138880
    CreateVolumeDownload:
      type: object
      additionalProperties: false
      required:
        - path
      properties:
        path:
          $ref: "#/components/schemas/VolumeFilePath"
    VolumeDownload:
      type: object
      additionalProperties: false
      required:
        - url
        - path
        - size
        - expires_at
      description: Temporary link to a volume file; the URL expires one hour after creation.
      properties:
        url:
          type: string
          format: uri
        path:
          $ref: "#/components/schemas/VolumeFilePath"
        size:
          type: integer
          minimum: 0
        expires_at:
          type: string
          format: date-time
    VolumeFilePrefix:
      type: string
      maxLength: 1024
      pattern: ^(?!/)(?!.*//)(?!\.{1,2}(?:/|$))(?!.*\/\.{1,2}(?:/|$))(?!.*\u0000).*$
      description: Relative volume path prefix; may be empty or end in `/`, but follows the same path safety rules as VolumeFilePath.
    VolumeFilePage:
      type: object
      additionalProperties: false
      required:
        - data
        - next_cursor
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/VolumeFile"
        next_cursor:
          type:
            - string
            - "null"
    VolumeFile:
      type: object
      additionalProperties: false
      required:
        - path
        - size
        - modified_at
      properties:
        path:
          $ref: "#/components/schemas/VolumeFilePath"
        size:
          type: integer
          minimum: 0
        modified_at:
          type: string
          format: date-time
    Sandbox:
      type: object
      description: |
        A sandbox created from a settled body, returned by create, restore, get and list.
        Required fields are present; fields with no value are null, never absent. Optional
        fields may be absent when their source is not known. Listings do no
        provider I/O: they show the latest durably recorded state, and native_state,
        route.selection and recorder are null. Detail responses observe the provider live and
        record the normalized state unless a concurrent lifecycle change supersedes the read.
      required:
        - id
        - state
        - state_reason
        - failure
        - native_state
        - observed_at
        - state_recorded_at
        - created_at
        - ready_at
        - ended_at
        - labels
        - lifetime
        - route
        - contract
        - request
        - hidden_secrets
        - tcp_ports
        - recorder
        - limits
      properties:
        id:
          type: string
          minLength: 1
          maxLength: 128
          pattern: ^[^\u0000]*$
        state:
          type: string
          enum:
            - creating
            - running
            - paused
            - destroying
            - destroyed
            - error
            - unknown
          x-boxrouter-open-enum: true
          description: |
            Clients must tolerate values they do not know. A sandbox the provider stopped, or one
            stopped for being idle that can be started again, reads paused; resume brings either back.
        state_reason:
          type:
            - string
            - "null"
          enum:
            - requested
            - lifetime
            - retention
            - idle_shutdown
            - external
            - null
          x-boxrouter-open-enum: true
          description: Why it is in this state. external is a change made at the provider, behind BoxRouter's back.
        failure:
          oneOf:
            - $ref: "#/components/schemas/SandboxFailure"
            - type: "null"
          description: |
            The provider failure recorded with state=error, or null when no recorded failure
            explains the current state. Present on create, detail and list responses; list
            reads return the durable value without calling the provider.
        native_state:
          type:
            - string
            - "null"
        observed_at:
          type: string
          format: date-time
          description: |
            Time the state source for this response was read: the owned-record lookup time
            for a list response, or the live provider-observation time for a detail response.
        state_recorded_at:
          type:
            - string
            - "null"
          format: date-time
          description: |
            Timestamp of the durable state source used for this response. This can be an
            existing lifecycle or lifetime record when the returned public state is derived
            from that source, and may predate observed_at. Null when no source timestamp is
            available or a concurrent lifecycle change supersedes the detail read.
        created_at:
          type:
            - string
            - "null"
          format: date-time
          description: Time Cloud sent the first create request when that edge is available; otherwise the sandbox record's created time.
        expires_at:
          type:
            - string
            - "null"
          format: date-time
          description: |
            Enforced deadline from the recorded lifetime budget, including extensions. This
            is the same deadline exposed as lifetime.ends_at; null when no deadline is known.
        ready_at:
          type:
            - string
            - "null"
          format: date-time
          description: |
            Time Cloud received the first create response; when that edge is unavailable, the sandbox
            record's ready time. Null while creating. List and detail responses use the same value.
        ended_at:
          type:
            - string
            - "null"
          format: date-time
          description: |
            Time Cloud received the last destroy response; when that edge is unavailable, Cloud's
            record of confirmation or first observation that the sandbox was gone. Null while the
            sandbox has not ended. List and detail responses use the same value.
        created_at_source:
          anyOf:
            - $ref: "#/components/schemas/UsageSource"
            - type: "null"
          description: Where `created_at` came from. Null when `created_at` is null. Always BoxRouter's own record.
        created_at_how:
          type:
            - string
            - "null"
          minLength: 1
          description: One sentence saying how `created_at` was determined. Null when `created_at` is null.
        created_at_step:
          type:
            - integer
            - "null"
          minimum: 1
          description: Null because `created_at` comes from a recorded lifecycle edge or Cloud's record.
        ready_at_source:
          anyOf:
            - $ref: "#/components/schemas/UsageSource"
            - type: "null"
          description: Where `ready_at` came from. Null when `ready_at` is null. Lifecycle edges and Cloud's own record are `recorded`.
        ready_at_how:
          type:
            - string
            - "null"
          minLength: 1
          description: One sentence saying how `ready_at` was determined. Null when `ready_at` is null.
        ready_at_step:
          type:
            - integer
            - "null"
          minimum: 1
          description: Null because `ready_at` comes from a recorded lifecycle edge or Cloud's record.
        ended_at_source:
          anyOf:
            - $ref: "#/components/schemas/UsageSource"
            - type: "null"
          description: Where `ended_at` came from. Null when `ended_at` is null. Lifecycle edges and Cloud's own record are `recorded`.
        ended_at_how:
          type:
            - string
            - "null"
          minLength: 1
          description: One sentence saying how `ended_at` was determined. Null when `ended_at` is null.
        ended_at_step:
          type:
            - integer
            - "null"
          minimum: 1
          description: Null because `ended_at` comes from a recorded lifecycle edge or Cloud's record.
        cost_source:
          anyOf:
            - $ref: "#/components/schemas/UsageSource"
            - type: "null"
          description: Where `cost` came from. Null when `cost` is null. `provider` for a reported cost, `calculated` for a worked-out one, `unknown` when no cost could be worked out. The `cost_step` is 1 for a provider figure, then 2 provider times, 3 a checked billing boundary, 4 a range, 5 unknown.
        cost_how:
          type:
            - string
            - "null"
          minLength: 1
          description: One sentence saying how `cost` was determined. Null when `cost` is null.
        cost_step:
          type:
            - integer
            - "null"
          minimum: 1
          description: |
            The position of the source in BoxRouter's source order for this value, 1 being the best.
            Null when the value is BoxRouter's own record rather than a pick between sources.
        origin:
          oneOf:
            - $ref: "#/components/schemas/SandboxOrigin"
            - type: "null"
          description: Durable creation provenance. Omitted when the source is not known.
        labels:
          type: object
          additionalProperties:
            type: string
        name:
          type:
            - string
            - "null"
          description: Sandbox name from labels.name, when set.
        resources:
          type: object
          description: Resolved CPU, memory and disk resources; null values mean unknown.
          properties:
            cpu:
              type:
                - number
                - "null"
              exclusiveMinimum: 0
            memory_mib:
              type:
                - integer
                - "null"
              minimum: 1
            disk_mib:
              type:
                - integer
                - "null"
              minimum: 1
        image:
          oneOf:
            - $ref: "#/components/schemas/SandboxImage"
            - type: "null"
          description: |
            The image the sandbox runs and where that is known from. Null when nothing records
            it. Absent from responses of a Cloud that does not report it yet.
        image_setup:
          $ref: "#/components/schemas/SandboxImageSetup"
          description: Present only when this start waited for image setup on its chosen provider.
        usage:
          anyOf:
            - $ref: "#/components/schemas/Usage"
            - $ref: "#/components/schemas/LegacyZeroGpuUsage"
            - type: "null"
          description: |
            What the sandbox used: its size, its running time per state, how often it was created,
            each fact with the source it came from. Null when nothing has been recorded or the
            response does not carry it. Absent from responses of a Cloud that does not report it yet.
        cost:
          oneOf:
            - $ref: "#/components/schemas/Cost"
            - $ref: "#/components/schemas/SandboxCostSummary"
            - type: "null"
          description: |
            What the sandbox cost, with one word saying how far to trust it. Create and detail
            responses carry the full cost with its working; list rows carry only its label and
            total, from the same calculation. One object serves both, so the schema accepts either
            shape anywhere: a client that needs the working reads the detail. Null when no cost has
            been worked out. Absent from responses of a Cloud that does not report it yet.
        charge:
          oneOf:
            - $ref: "#/components/schemas/SandboxCharge"
            - type: "null"
          description: |
            What BoxRouter charged to the workspace's credits for this sandbox: the reservation
            while it runs, then the amount charged once the provider's records are in. Present
            on credit-funded sandboxes in detail and list responses. Null for a sandbox paid with
            the workspace's own provider account (BYOK), for a credit-funded one whose
            reservation was released without a charge, and for one whose ledger record cannot be
            verified: BoxRouter reports no charge rather than a guess. `cost` still reports the
            provider's cost. Absent from create and other responses, and from a Cloud that does
            not report it yet. It is the ledger's amount, never derived from `cost` or from
            BoxRouter's clock.
        funded_until:
          type:
            - string
            - "null"
          format: date-time
          description: |
            The time up to which the workspace's credits are reserved for this sandbox. A sandbox
            created without `lifetime_seconds` renews its reservation hourly while the balance
            covers it, so this moves later at each renewal; the sandbox ends at this time if the
            balance could not cover another hour. For a sandbox created with `lifetime_seconds`
            it is the end of that lifetime. Null for a sandbox paid with the workspace's own
            provider account (BYOK), which has no reservation. A reservation is a hold on the
            balance, never a cost: `charge` reports the credits reserved and then charged. Present
            on create and on detail and list reads; absent from a Cloud that does not report it yet.
        connectable:
          type: boolean
          description: |
            Whether interactive access to this sandbox is offered: a terminal, commands, files and
            processes. False for a sandbox an Actions job runs in, which holds that job's token and
            secrets; Cloud refuses these operations on it, before and after the job ends, and the
            dashboard shows no Connect action. True for every other sandbox. One rule, decided by
            Cloud; clients do not infer it from `origin`, which an Actions runner that has not yet
            served a job does not have. Present on detail and list reads; absent from a Cloud that
            does not report it yet.
        lifetime:
          $ref: "#/components/schemas/Lifetime"
        route:
          $ref: "#/components/schemas/SandboxRoute"
        contract:
          oneOf:
            - $ref: "#/components/schemas/Contract"
            - type: "null"
          description: Null for a record with no recorded contract, such as one a provider-native route created.
        request:
          $ref: "#/components/schemas/SandboxRequestSnapshot"
        hidden_secrets:
          type:
            - object
            - "null"
          propertyNames:
            $ref: "#/components/schemas/SecretName"
          additionalProperties:
            $ref: "#/components/schemas/HiddenSecretResult"
          description: |
            Public metadata for the request-only secret values, keyed by their submitted names.
            Each result contains only a placeholder and its allowed hosts. Null when the create
            submitted no secret values.
        tcp_ports:
          type:
            - array
            - "null"
          items:
            $ref: "#/components/schemas/TcpPortEndpoint"
          description: Public host and port for each requested TCP listener, in request order.
        recorder:
          type:
            - object
            - "null"
          description: |
            A tally of the gate calls made on this sandbox, allowed and refused, with the
            requirements line those calls relied on. It only tallies: it switches no gate on.
        limits:
          $ref: "#/components/schemas/SandboxLimits"
        volume:
          oneOf:
            - $ref: "#/components/schemas/VolumeAttachment"
            - type: "null"
          description: Present when the sandbox is attached to a BoxRouter Volume.
        operation_id:
          type:
            - string
            - "null"
          minLength: 1
          description: |
            Present on a destroy response: the workspace-owned destroy operation while lifecycle
            work remains (`state` is `destroying`), null once the destroy is confirmed. Absent
            from every other response.
        account:
          type: object
          required:
            - healthy
            - detail
          description: |
            Present only while the account's key no longer works (section 7); that sandbox's
            calls then answer account_unhealthy and state is unknown.
          properties:
            healthy:
              type: boolean
            detail:
              type:
                - string
                - "null"
    SandboxFailure:
      type: object
      additionalProperties: false
      required:
        - summary
        - provider_state
        - native_code
        - native_message
        - observed_at
      description: |
        The most recent provider failure recorded with the sandbox's state. The summary is
        BoxRouter-authored plain language. `provider_state` is the raw provider status
        and is not covered by the sanitization guarantee; `native_code` and
        `native_message` contain only allowlisted, sanitized values. Repeated observations of the same failure keep the first
        observed_at; a changed failure gets a new timestamp. This object is stored with
        the state, cleared when the sandbox leaves error, and unchanged by unreadable
        provider observations.
      properties:
        summary:
          type: string
          minLength: 1
          maxLength: 200
          description: |
            BoxRouter-authored plain language chosen from a fixed set keyed by provider and
            provider_state. An unrecognised status uses "The provider reported this sandbox
            as failed."; it never guesses a cause.
        provider_state:
          type:
            - string
            - "null"
          description: The provider's raw failure status, the same value native_state carries, or null if none was supplied.
        native_code:
          type:
            - string
            - "null"
          pattern: ^[A-Za-z0-9_.:-]{1,64}$
          description: The provider's short structured failure code, or null if absent or unsafe.
        native_message:
          type:
            - string
            - "null"
          maxLength: 512
          description: The provider's diagnostic text after redaction, or null if absent or unsafe.
        observed_at:
          type: string
          format: date-time
          description: When BoxRouter first observed this specific failure.
    UsageSource:
      description: "Where a fact came from. provider: the provider's API told us, about this sandbox. published: the provider's docs or pricing page, not about this sandbox in particular. measured: BoxRouter checked it itself with a clock or in its own sandboxes. recorded: BoxRouter's own record of something it did or saw. calculated: worked out from other facts, each with its own source. unknown: nobody told us and we could not check."
      type: string
      enum:
        - provider
        - published
        - measured
        - recorded
        - calculated
        - unknown
    SandboxOrigin:
      description: |
        What created the sandbox, discriminated by source. github_actions is a BoxRouter
        Actions job; agents is an agent launch (POST /launches). Sandboxes created directly
        through the API have no origin.
      oneOf:
        - $ref: "#/components/schemas/ActionsSandboxOrigin"
        - $ref: "#/components/schemas/AgentsSandboxOrigin"
      discriminator:
        propertyName: source
        mapping:
          github_actions: "#/components/schemas/ActionsSandboxOrigin"
          agents: "#/components/schemas/AgentsSandboxOrigin"
    ActionsSandboxOrigin:
      type: object
      description: |
        A sandbox a BoxRouter Actions job ran in. source, run_id and job_id come from the
        placement and are always present; every other fact is omitted when Cloud does not
        know it.
      additionalProperties: false
      required:
        - source
        - run_id
        - job_id
      properties:
        source:
          type: string
          enum:
            - github_actions
        repository:
          type: string
          minLength: 1
          description: |
            owner/name, from the job's GitHub webhook; for jobs admitted before the webhook was
            recorded, from the workspace's GitHub repository binding for the same repository ID.
        workflow:
          type:
            - string
            - "null"
          minLength: 1
          description: Workflow name from the job's GitHub webhook or run.
        run_id:
          type: string
          minLength: 1
          maxLength: 128
          pattern: ^[^\u0000]*$
          description: BoxRouter Actions run ID used by the Actions dashboard deep link.
        run_number:
          type:
            - integer
            - "null"
          minimum: 1
          description: GitHub run number from the run.
        job_id:
          type: string
          minLength: 1
          maxLength: 128
          pattern: ^[^\u0000]*$
          description: BoxRouter Actions job ID.
        job_name:
          type: string
          minLength: 1
          description: Job name from the job's GitHub webhook.
        run_url:
          type: string
          format: uri
          description: GitHub run page, built from repository and the GitHub run ID.
        job_url:
          type: string
          format: uri
          description: |
            GitHub job page: the job's html_url from its webhook, else built from repository and
            the GitHub run and job IDs.
    AgentsSandboxOrigin:
      type: object
      description: A sandbox created by an agent launch.
      additionalProperties: false
      required:
        - source
        - launch_id
        - agent
        - agent_name
      properties:
        source:
          type: string
          enum:
            - agents
        launch_id:
          type: string
          minLength: 1
          maxLength: 128
          pattern: ^[^\u0000]*$
          description: Launch operation ID, as in GET /launches/{operation_id}.
        agent:
          type: string
          minLength: 1
          description: The launched manifest's name; the agent catalog ID for catalog agents, such as claude-code.
        agent_name:
          type: string
          minLength: 1
          description: Display name from the agent catalog, such as Claude Code; the manifest name for agents outside the catalog.
        repository:
          type:
            - string
            - "null"
          minLength: 1
          description: Repository as owner/name when the launch checked out code; null or absent when it had none.
        sha:
          type:
            - string
            - "null"
          pattern: ^[0-9a-f]{40}$
          description: Commit checked out for the launch; null or absent when it had no code.
    SandboxImage:
      type: object
      additionalProperties: false
      required:
        - value
        - source
      description: |
        The image a sandbox runs, with where that is known from: `recorded` for what the creation
        request asked for, `provider` where the provider reported which image or template it ran.
      properties:
        value:
          type: string
          minLength: 1
          description: |
            `standard` when the request omitted `image` (BoxRouter's standard image), `provider_default`
            when it opted out, `dockerfile` for an image built from a Dockerfile, or else the OCI
            reference or provider artifact the request named.
        source:
          $ref: "#/components/schemas/UsageSource"
    SandboxImageSetup:
      type: object
      required:
        - provider
        - duration_ms
      properties:
        provider:
          type: string
          minLength: 1
        duration_ms:
          type: integer
          minimum: 0
    Usage:
      title: Usage record
      description: What one sandbox used, as a collector saw it. One record is one coherent set of inputs for the calculator; where two sources disagree, a collector builds one record per source. Every fact carries where it came from, when it was observed and how. A fact nobody could establish has source `unknown` and a null value; it is never left out or filled with a default. `provider`, the record's key, is the only value without provenance.
      type: object
      additionalProperties: false
      required:
        - provider
        - started
        - region
        - size
        - intervals
        - creations
      properties:
        provider:
          description: The provider that ran the sandbox, as named in its price versions.
          type: string
          pattern: ^[a-z][a-z0-9-]*$
        class:
          description: The sandbox's class or runtime, which picks the rate set in the price version. If absent, it may be priced only when the price version has exactly one class; otherwise the cost is unknown.
          type: object
          additionalProperties: false
          required:
            - name
            - source
            - observed_at
            - how
          properties:
            name:
              type:
                - string
                - "null"
              minLength: 1
            source:
              $ref: "#/components/schemas/UsageSource"
            observed_at:
              $ref: "#/components/schemas/PricingTimestamp"
            how:
              $ref: "#/components/schemas/UsageHow"
          if:
            $ref: "#/components/schemas/UsageIsUnknown"
          then:
            properties:
              name:
                type: "null"
          else:
            properties:
              name:
                type: string
        started:
          description: When the sandbox started. It picks the price version in effect.
          type: object
          additionalProperties: false
          required:
            - at
            - source
            - observed_at
            - how
          properties:
            at:
              type:
                - string
                - "null"
            source:
              $ref: "#/components/schemas/UsageSource"
            observed_at:
              $ref: "#/components/schemas/PricingTimestamp"
            how:
              $ref: "#/components/schemas/UsageHow"
          if:
            $ref: "#/components/schemas/UsageIsUnknown"
          then:
            properties:
              at:
                type: "null"
          else:
            properties:
              at:
                $ref: "#/components/schemas/PricingTimestamp"
        region:
          description: The region the sandbox ran in, as the provider names it.
          type: object
          additionalProperties: false
          required:
            - name
            - source
            - observed_at
            - how
          properties:
            name:
              type:
                - string
                - "null"
              minLength: 1
            source:
              $ref: "#/components/schemas/UsageSource"
            observed_at:
              $ref: "#/components/schemas/PricingTimestamp"
            how:
              $ref: "#/components/schemas/UsageHow"
          if:
            $ref: "#/components/schemas/UsageIsUnknown"
          then:
            properties:
              name:
                type: "null"
          else:
            properties:
              name:
                type: string
        size:
          description: What the sandbox got. Each field is optional because a source may give only some of them; at least one is present unless the source is `unknown`. When GPUs are present, `gpu` and `gpu_model` are both recorded; without a GPU, both are absent.
          type: object
          additionalProperties: false
          required:
            - source
            - observed_at
            - how
          properties:
            cpu:
              description: vCPUs.
              type: number
              exclusiveMinimum: 0
            memory_mib:
              type: integer
              minimum: 1
            disk_mib:
              type: integer
              minimum: 1
            gpu:
              description: Number of allocated GPUs.
              type: integer
              minimum: 1
            gpu_model:
              description: The allocated model's gpu-registry identifier.
              type: string
              pattern: ^[a-z][a-z0-9-]*:[a-z0-9][a-z0-9-]*$
            source:
              $ref: "#/components/schemas/UsageSource"
            observed_at:
              $ref: "#/components/schemas/PricingTimestamp"
            how:
              $ref: "#/components/schemas/UsageHow"
          if:
            $ref: "#/components/schemas/UsageIsUnknown"
          then:
            not:
              anyOf:
                - required:
                    - cpu
                - required:
                    - memory_mib
                - required:
                    - disk_mib
                - required:
                    - gpu
                - required:
                    - gpu_model
          else:
            anyOf:
              - required:
                  - cpu
              - required:
                  - memory_mib
              - required:
                  - disk_mib
              - required:
                  - gpu
          allOf:
            - if:
                required:
                  - gpu
              then:
                required:
                  - gpu_model
            - if:
                required:
                  - gpu_model
              then:
                required:
                  - gpu
        intervals:
          description: Time in each billed state, one entry per period, in time order. A price version's minimum and increment rules apply to each entry.
          type: array
          minItems: 1
          items:
            type: object
            additionalProperties: false
            required:
              - state
              - seconds
              - source
              - observed_at
              - how
            properties:
              size:
                $ref: "#/components/schemas/UsageSize"
              snapshot_storage:
                $ref: "#/components/schemas/UsageSnapshotStorage"
              state:
                enum:
                  - running
                  - paused
                  - stopped
              seconds:
                description: Exact seconds, or a range when a change was noticed late and the period is only bounded.
                oneOf:
                  - type: number
                    minimum: 0
                  - $ref: "#/components/schemas/UsageRange"
                  - type: "null"
              source:
                $ref: "#/components/schemas/UsageSource"
              observed_at:
                $ref: "#/components/schemas/PricingTimestamp"
              how:
                $ref: "#/components/schemas/UsageHow"
            if:
              $ref: "#/components/schemas/UsageIsUnknown"
            then:
              properties:
                seconds:
                  type: "null"
            else:
              properties:
                seconds:
                  type:
                    - number
                    - object
        creations:
          description: "How many times the sandbox was created: 1, plus any restores that the provider charges as a creation."
          type: object
          additionalProperties: false
          required:
            - count
            - source
            - observed_at
            - how
          properties:
            count:
              type:
                - integer
                - "null"
              minimum: 0
            source:
              $ref: "#/components/schemas/UsageSource"
            observed_at:
              $ref: "#/components/schemas/PricingTimestamp"
            how:
              $ref: "#/components/schemas/UsageHow"
          if:
            $ref: "#/components/schemas/UsageIsUnknown"
          then:
            properties:
              count:
                type: "null"
          else:
            properties:
              count:
                type: integer
        active_cpu:
          description: Seconds of CPU the sandbox actually used, for providers that bill active CPU. Absent where the provider does not bill it.
          type: object
          additionalProperties: false
          required:
            - seconds
            - source
            - observed_at
            - how
          properties:
            seconds:
              type:
                - number
                - "null"
              minimum: 0
            source:
              $ref: "#/components/schemas/UsageSource"
            observed_at:
              $ref: "#/components/schemas/PricingTimestamp"
            how:
              $ref: "#/components/schemas/UsageHow"
          if:
            $ref: "#/components/schemas/UsageIsUnknown"
          then:
            properties:
              seconds:
                type: "null"
          else:
            properties:
              seconds:
                type: number
        egress:
          description: Bytes the sandbox sent out. Absent where the provider does not bill it.
          type: object
          additionalProperties: false
          required:
            - bytes
            - source
            - observed_at
            - how
          properties:
            bytes:
              type:
                - integer
                - "null"
              minimum: 0
            source:
              $ref: "#/components/schemas/UsageSource"
            observed_at:
              $ref: "#/components/schemas/PricingTimestamp"
            how:
              $ref: "#/components/schemas/UsageHow"
          if:
            $ref: "#/components/schemas/UsageIsUnknown"
          then:
            properties:
              bytes:
                type: "null"
          else:
            properties:
              bytes:
                type: integer
        snapshot_storage:
          description: The stored snapshot size, or its known maximum when the provider does not report the actual size.
          type: object
          additionalProperties: false
          required:
            - source
            - observed_at
            - how
          properties:
            gb:
              type:
                - number
                - "null"
              minimum: 0
            max_gb:
              type:
                - number
                - "null"
              minimum: 0
            source:
              $ref: "#/components/schemas/UsageSource"
            observed_at:
              $ref: "#/components/schemas/PricingTimestamp"
            how:
              $ref: "#/components/schemas/UsageHow"
            bytes:
              description: Provider snapshot sizeBytes, kept as an exact integer.
              type: integer
              minimum: 0
          if:
            $ref: "#/components/schemas/UsageIsUnknown"
          then:
            properties:
              gb:
                type: "null"
          else:
            anyOf:
              - required:
                  - gb
                properties:
                  gb:
                    type: number
              - required:
                  - bytes
    UsageHow:
      description: "How the fact was obtained: the API call, the page URL, the record, or why it is unknown."
      type: string
      minLength: 1
    UsageIsUnknown:
      properties:
        source:
          const: unknown
      required:
        - source
    UsageSize:
      description: What the sandbox got. Each field is optional because a source may give only some of them; at least one is present unless the source is `unknown`. When GPUs are present, `gpu` and `gpu_model` are both recorded; without a GPU, both are absent.
      type: object
      additionalProperties: false
      required:
        - source
        - observed_at
        - how
      properties:
        cpu:
          description: vCPUs.
          type: number
          exclusiveMinimum: 0
        memory_mib:
          type: integer
          minimum: 1
        disk_mib:
          type: integer
          minimum: 1
        gpu:
          description: Number of allocated GPUs.
          type: integer
          minimum: 1
        gpu_model:
          description: The allocated model's gpu-registry identifier.
          type: string
          pattern: ^[a-z][a-z0-9-]*:[a-z0-9][a-z0-9-]*$
        source:
          $ref: "#/components/schemas/UsageSource"
        observed_at:
          $ref: "#/components/schemas/PricingTimestamp"
        how:
          $ref: "#/components/schemas/UsageHow"
      if:
        $ref: "#/components/schemas/UsageIsUnknown"
      then:
        not:
          anyOf:
            - required:
                - cpu
            - required:
                - memory_mib
            - required:
                - disk_mib
            - required:
                - gpu
            - required:
                - gpu_model
      else:
        anyOf:
          - required:
              - cpu
          - required:
              - memory_mib
          - required:
              - disk_mib
          - required:
              - gpu
      allOf:
        - if:
            required:
              - gpu
          then:
            required:
              - gpu_model
        - if:
            required:
              - gpu_model
          then:
            required:
              - gpu
    UsageSnapshotStorage:
      description: The stored snapshot size, or its known maximum when the provider does not report the actual size.
      type: object
      additionalProperties: false
      required:
        - source
        - observed_at
        - how
      properties:
        gb:
          type:
            - number
            - "null"
          minimum: 0
        max_gb:
          type:
            - number
            - "null"
          minimum: 0
        source:
          $ref: "#/components/schemas/UsageSource"
        observed_at:
          $ref: "#/components/schemas/PricingTimestamp"
        how:
          $ref: "#/components/schemas/UsageHow"
        bytes:
          description: Provider snapshot sizeBytes, kept as an exact integer.
          type: integer
          minimum: 0
      if:
        $ref: "#/components/schemas/UsageIsUnknown"
      then:
        properties:
          gb:
            type: "null"
      else:
        anyOf:
          - required:
              - gb
            properties:
              gb:
                type: number
          - required:
              - bytes
    UsageRange:
      description: A bounded quantity.
      type: object
      additionalProperties: false
      required:
        - low
        - high
      properties:
        low:
          type: number
          minimum: 0
        high:
          type: number
          minimum: 0
    LegacyZeroGpuUsage:
      type: object
      additionalProperties: false
      required:
        - provider
        - started
        - region
        - size
        - intervals
        - creations
      properties:
        provider:
          $ref: "#/components/schemas/provider"
        class:
          $ref: "#/components/schemas/class"
        started:
          $ref: "#/components/schemas/started"
        region:
          $ref: "#/components/schemas/region"
        size:
          type: object
          additionalProperties: false
          required:
            - source
            - observed_at
            - how
          properties:
            cpu:
              $ref: "#/components/schemas/cpu"
            memory_mib:
              $ref: "#/components/schemas/memory_mib"
            disk_mib:
              $ref: "#/components/schemas/disk_mib"
            gpu:
              const: 0
            gpu_model:
              $ref: "#/components/schemas/gpu_model"
            source:
              $ref: "#/components/schemas/UsageSource"
            observed_at:
              $ref: "#/components/schemas/PricingTimestamp"
            how:
              $ref: "#/components/schemas/UsageHow"
          if:
            $ref: "#/components/schemas/UsageIsUnknown"
          then:
            $ref: "#/components/schemas/then"
          else:
            $ref: "#/components/schemas/else"
          allOf:
            - if:
                required:
                  - gpu
              then:
                required:
                  - gpu_model
            - if:
                required:
                  - gpu_model
              then:
                required:
                  - gpu
        intervals:
          $ref: "#/components/schemas/intervals"
        creations:
          $ref: "#/components/schemas/creations"
        active_cpu:
          $ref: "#/components/schemas/active_cpu"
        egress:
          $ref: "#/components/schemas/egress"
    provider:
      description: The provider that ran the sandbox, as named in its price versions.
      type: string
      pattern: ^[a-z][a-z0-9-]*$
    class:
      description: The sandbox's class or runtime, which picks the rate set in the price version. If absent, it may be priced only when the price version has exactly one class; otherwise the cost is unknown.
      type: object
      additionalProperties: false
      required:
        - name
        - source
        - observed_at
        - how
      properties:
        name:
          type:
            - string
            - "null"
          minLength: 1
        source:
          $ref: "#/components/schemas/UsageSource"
        observed_at:
          $ref: "#/components/schemas/PricingTimestamp"
        how:
          $ref: "#/components/schemas/UsageHow"
      if:
        $ref: "#/components/schemas/UsageIsUnknown"
      then:
        properties:
          name:
            type: "null"
      else:
        properties:
          name:
            type: string
    started:
      description: When the sandbox started. It picks the price version in effect.
      type: object
      additionalProperties: false
      required:
        - at
        - source
        - observed_at
        - how
      properties:
        at:
          type:
            - string
            - "null"
        source:
          $ref: "#/components/schemas/UsageSource"
        observed_at:
          $ref: "#/components/schemas/PricingTimestamp"
        how:
          $ref: "#/components/schemas/UsageHow"
      if:
        $ref: "#/components/schemas/UsageIsUnknown"
      then:
        properties:
          at:
            type: "null"
      else:
        properties:
          at:
            $ref: "#/components/schemas/PricingTimestamp"
    region:
      description: The region the sandbox ran in, as the provider names it.
      type: object
      additionalProperties: false
      required:
        - name
        - source
        - observed_at
        - how
      properties:
        name:
          type:
            - string
            - "null"
          minLength: 1
        source:
          $ref: "#/components/schemas/UsageSource"
        observed_at:
          $ref: "#/components/schemas/PricingTimestamp"
        how:
          $ref: "#/components/schemas/UsageHow"
      if:
        $ref: "#/components/schemas/UsageIsUnknown"
      then:
        properties:
          name:
            type: "null"
      else:
        properties:
          name:
            type: string
    cpu:
      description: vCPUs.
      type: number
      exclusiveMinimum: 0
    memory_mib:
      type: integer
      minimum: 1
    disk_mib:
      type: integer
      minimum: 1
    gpu_model:
      description: The allocated model's gpu-registry identifier.
      type: string
      pattern: ^[a-z][a-z0-9-]*:[a-z0-9][a-z0-9-]*$
    then:
      not:
        anyOf:
          - required:
              - cpu
          - required:
              - memory_mib
          - required:
              - disk_mib
          - required:
              - gpu
          - required:
              - gpu_model
    else:
      anyOf:
        - required:
            - cpu
        - required:
            - memory_mib
        - required:
            - disk_mib
        - required:
            - gpu
    intervals:
      description: Time in each billed state, one entry per period, in time order. A price version's minimum and increment rules apply to each entry.
      type: array
      minItems: 1
      items:
        type: object
        additionalProperties: false
        required:
          - state
          - seconds
          - source
          - observed_at
          - how
        properties:
          size:
            $ref: "#/components/schemas/UsageSize"
          snapshot_storage:
            $ref: "#/components/schemas/UsageSnapshotStorage"
          state:
            enum:
              - running
              - paused
              - stopped
          seconds:
            description: Exact seconds, or a range when a change was noticed late and the period is only bounded.
            oneOf:
              - type: number
                minimum: 0
              - $ref: "#/components/schemas/UsageRange"
              - type: "null"
          source:
            $ref: "#/components/schemas/UsageSource"
          observed_at:
            $ref: "#/components/schemas/PricingTimestamp"
          how:
            $ref: "#/components/schemas/UsageHow"
        if:
          $ref: "#/components/schemas/UsageIsUnknown"
        then:
          properties:
            seconds:
              type: "null"
        else:
          properties:
            seconds:
              type:
                - number
                - object
    creations:
      description: "How many times the sandbox was created: 1, plus any restores that the provider charges as a creation."
      type: object
      additionalProperties: false
      required:
        - count
        - source
        - observed_at
        - how
      properties:
        count:
          type:
            - integer
            - "null"
          minimum: 0
        source:
          $ref: "#/components/schemas/UsageSource"
        observed_at:
          $ref: "#/components/schemas/PricingTimestamp"
        how:
          $ref: "#/components/schemas/UsageHow"
      if:
        $ref: "#/components/schemas/UsageIsUnknown"
      then:
        properties:
          count:
            type: "null"
      else:
        properties:
          count:
            type: integer
    active_cpu:
      description: Seconds of CPU the sandbox actually used, for providers that bill active CPU. Absent where the provider does not bill it.
      type: object
      additionalProperties: false
      required:
        - seconds
        - source
        - observed_at
        - how
      properties:
        seconds:
          type:
            - number
            - "null"
          minimum: 0
        source:
          $ref: "#/components/schemas/UsageSource"
        observed_at:
          $ref: "#/components/schemas/PricingTimestamp"
        how:
          $ref: "#/components/schemas/UsageHow"
      if:
        $ref: "#/components/schemas/UsageIsUnknown"
      then:
        properties:
          seconds:
            type: "null"
      else:
        properties:
          seconds:
            type: number
    egress:
      description: Bytes the sandbox sent out. Absent where the provider does not bill it.
      type: object
      additionalProperties: false
      required:
        - bytes
        - source
        - observed_at
        - how
      properties:
        bytes:
          type:
            - integer
            - "null"
          minimum: 0
        source:
          $ref: "#/components/schemas/UsageSource"
        observed_at:
          $ref: "#/components/schemas/PricingTimestamp"
        how:
          $ref: "#/components/schemas/UsageHow"
      if:
        $ref: "#/components/schemas/UsageIsUnknown"
      then:
        properties:
          bytes:
            type: "null"
      else:
        properties:
          bytes:
            type: integer
    SandboxCharge:
      type: object
      additionalProperties: false
      required:
        - status
        - reserved_nanodollars
        - held_nanodollars
        - lifetime_seconds
      description: |
        The credit ledger's account of one credit-funded sandbox, in billionths of a US dollar as
        exact decimal strings. `reserved` while it runs: the reservation is held and nothing is
        charged. `pending` once it has ended and the provider's records are not in: the charge
        is set from them, the reservation stays held, and no amount is reported until they
        arrive. `charged` once the records set the charge: `amount_nanodollars` is the current
        charge and `first_charged_nanodollars` the charge as first settled. A later correction
        moves the charge by its `delta_nanodollars` and never reopens the reservation.
      properties:
        status:
          type: string
          enum:
            - reserved
            - pending
            - charged
          x-boxrouter-open-enum: true
          description: Clients must handle unknown values explicitly.
        reserved_nanodollars:
          type: string
          maxLength: 19
          pattern: ^(0|[1-9][0-9]{0,18})$
          description: The credit reserved when the sandbox started, for the lifetime it asked for.
        held_nanodollars:
          type: string
          maxLength: 19
          pattern: ^(0|[1-9][0-9]{0,18})$
          description: What is still reserved from the balance for this sandbox. Zero once it is charged.
        lifetime_seconds:
          type:
            - integer
            - "null"
          minimum: 1
          description: The lifetime the reservation was made for. Null when it is not recorded.
        funding:
          type: string
          enum:
            - fixed
            - rolling
            - ending_unfunded
          x-boxrouter-open-enum: true
          description: |
            How the sandbox's credits are reserved. `fixed`: all of the lifetime asked for was
            reserved when the sandbox started. `rolling`: the sandbox was created without a lifetime and
            credits are reserved one hour at a time, renewed before each hour ends; `funded_until` on
            the sandbox says how far it is funded. `ending_unfunded`: a renewal could not be
            funded from the balance, so the sandbox ends at `funded_until`. Clients must handle
            unknown values explicitly. Absent from a Cloud that does not report it yet.
        first_charged_nanodollars:
          type: string
          maxLength: 19
          pattern: ^(0|[1-9][0-9]{0,18})$
          description: The charge as first settled from the provider's records. Present only when charged.
        amount_nanodollars:
          type: string
          maxLength: 19
          pattern: ^(0|[1-9][0-9]{0,18})$
          description: |
            The charge now: the first charge moved by every correction. Present only when charged;
            a pending or reserved charge carries no amount.
        returned_nanodollars:
          type: string
          maxLength: 19
          pattern: ^(0|[1-9][0-9]{0,18})$
          description: What went back to the balance when the first charge was settled, the reservation less that charge. Present only when charged.
        create_fee_nanodollars:
          type: string
          maxLength: 19
          pattern: ^(0|[1-9][0-9]{0,18})$
          description: |
            The flat per-create fee the price book added to the first charge; zero while no book
            sets one. It is inside `amount_nanodollars`, not on top of it. Present only when
            charged, and absent from a Cloud that does not report it yet.
        basis:
          type:
            - string
            - "null"
          enum:
            - reported
            - calculated
            - null
          x-boxrouter-open-enum: true
          description: |
            How the provider's cost for this sandbox was established, as `cost.label` says for it:
            `reported` for the provider's own figure, `calculated` for one worked out from its
            usage records. Null when the cost's source is not known. Present only when charged.
        settled_at:
          type: string
          format: date-time
          description: When the first charge was recorded in the ledger. Present only when charged.
        corrections:
          type: array
          maxItems: 1000
          description: |
            Later changes to the charge, oldest first, when the provider's records or reported cost
            for this sandbox changed after it was first charged. Independent credits an operator
            gives are not corrections. Present only when charged; empty when none.
          items:
            $ref: "#/components/schemas/SandboxChargeCorrection"
      allOf:
        - if:
            properties:
              status:
                const: charged
            required:
              - status
          then:
            required:
              - first_charged_nanodollars
              - amount_nanodollars
              - returned_nanodollars
              - settled_at
              - corrections
          else:
            properties:
              first_charged_nanodollars: false
              amount_nanodollars: false
              returned_nanodollars: false
              create_fee_nanodollars: false
              basis: false
              settled_at: false
              corrections: false
    SandboxChargeCorrection:
      type: object
      additionalProperties: false
      required:
        - at
        - delta_nanodollars
        - reason
      properties:
        at:
          type: string
          format: date-time
          description: When the ledger recorded the correction.
        delta_nanodollars:
          type: string
          maxLength: 20
          pattern: ^(-?[1-9][0-9]{0,18})$
          description: The change to the charge. Positive means the charge went up and the difference was taken from the balance; negative means it went down and the difference was returned to it.
        reason:
          type: string
          enum:
            - provider_revised
          x-boxrouter-open-enum: true
          description: |
            Why the charge moved. `provider_revised`: the provider's records or reported cost for
            this sandbox changed. Clients must handle unknown values explicitly.
    SandboxRoute:
      type: object
      required:
        - provider
        - class
        - adapter_version
        - selection
      description: |
        The adapter that made this sandbox. Support questions start at adapter_version. A sandbox
        with no recorded contract still names its provider and account; its class and
        adapter_version are null.
      properties:
        id:
          type:
            - string
            - "null"
        provider:
          type: string
        class:
          type:
            - string
            - "null"
          description: The declaration's class, such as a provider's VM class; null when the adapter publishes one declaration.
        adapter_version:
          type:
            - string
            - "null"
        account_id:
          type:
            - string
            - "null"
        supply:
          type:
            - string
            - "null"
        region:
          type:
            - string
            - "null"
        native_id:
          type:
            - string
            - "null"
          description: Provider-owned sandbox identifier; null when unavailable.
        selection:
          oneOf:
            - $ref: "#/components/schemas/SandboxRouteSelection"
            - type: "null"
          description: Null in listings, which look at no route.
    SandboxRouteSelection:
      type: object
      required:
        - considered
        - eligible
        - ranked_by
        - excluded
        - attempts
      description: Which routes were considered, how they were ranked and why each other one was excluded.
      properties:
        considered:
          type: integer
          minimum: 0
        eligible:
          type: integer
          minimum: 0
        ranked_by:
          type:
            - string
            - "null"
          enum:
            - price
            - time
            - providers
            - order
            - null
          x-boxrouter-choices:
            price:
              label: Price
              line: Lowest hourly price first.
              measure: price
              better: lower
            time:
              label: Time
              line: Shortest run time measured on reference workloads first.
              measure: time
              better: lower
            providers:
              label: Providers
              line: The hard list order.
            order:
              label: Order
              line: A soft provider preference.
          description: The sort used; providers for the hard list order; order for a soft provider preference; or null for the router's default.
        excluded:
          type: array
          items:
            $ref: "#/components/schemas/ExcludedRoute"
        preparation_skips:
          type: array
          description: Routes skipped after eligibility because image or provider preparation failed.
          items:
            $ref: "#/components/schemas/CreationPreparationSkip"
        attempts:
          type: array
          description: |
            Adapters that refused this create before the one that made it, in dispatch order. Only
            dispatches appear; routes skipped before dispatch are in `excluded`. Normally empty:
            BoxRouter tries the next eligible adapter once when a refusal is definite.
          items:
            type: object
            required:
              - provider
              - native_code
              - native_status
            properties:
              provider:
                type: string
              native_code:
                type:
                  - string
                  - "null"
              native_status:
                type:
                  - integer
                  - "null"
              route_id:
                type:
                  - string
                  - "null"
                maxLength: 128
                description: The route this dispatch used.
              account_id:
                type:
                  - string
                  - "null"
                maxLength: 128
                description: The connected account this dispatch used.
              supply:
                type:
                  - string
                  - "null"
                maxLength: 32
                description: "The funding authority of this dispatch: byok or managed."
              cause:
                $ref: "#/components/schemas/CreationRouteCause"
        fallbacks_allowed:
          type: boolean
          description: The effective allow_fallbacks after inheritance. Absent when the create predates this field.
        stopped_reason:
          $ref: "#/components/schemas/CreationStoppedReason"
        funding_group:
          $ref: "#/components/schemas/FundingGroup"
          description: Frozen payer group of the selected route. Absent on older responses; definite pre-create fallback stays in this group after dispatch.
        workspace_mode:
          type: string
          enum:
            - automatic
            - cheapest
            - fastest
            - custom
          x-boxrouter-open-enum: true
          x-boxrouter-choices:
            automatic:
              label: Auto
              line: A random draw weighted toward lower hourly price.
            cheapest:
              label: Cheapest
              line: Lowest hourly price first.
              measure: price
              better: lower
            fastest:
              label: Fastest
              line: Shortest run time measured on reference workloads first.
              measure: time
              better: lower
            custom:
              label: Custom
              line: Your exact provider order, top to bottom.
          description: Effective workspace mode at admission; absent on older responses.
    ExcludedRoute:
      type: object
      required:
        - provider
        - class
        - account_id
        - failed
      description: One candidate that was not eligible, with every check it failed.
      properties:
        provider:
          type: string
        class:
          type:
            - string
            - "null"
        account_id:
          type:
            - string
            - "null"
        failed:
          type: array
          items:
            $ref: "#/components/schemas/FailedCheck"
    FailedCheck:
      type: object
      required:
        - reason
      description: |
        One failed check, in the order they are made: baseline, then the gates in the spec's
        order with extras last, then conflicts, resources and basis.
      properties:
        reason:
          type: string
          description: |
            gate_not_listed, gate_unsupported, gate_unavailable, knob_mismatch, knob_unknown,
            conflict, resource_mismatch, resource_unknown, not_proven, limit_exceeded,
            bound_elsewhere, extra_mismatch, declaration_unusable, variant_unusable,
            baseline_failed, account_unchecked, account_unhealthy, standard_image_unavailable,
            not_allowed, `not connected`, `not enabled`, price_unknown, insufficient_credit,
            spend_limit_exceeded, outside_funding_authority, unfunded or class_not_selected.
            `standard_image_unavailable` means the account has no verified copy of the standard image
            for the route's exact region, image version and digest; its `fix` field gives the setup fix.
            It is distinct from `baseline_failed`, which means the provider cannot meet the requested
            contract. `price_unknown` means the payer's price is unknown; `insufficient_credit` means
            the payer has fewer credits than required; `spend_limit_exceeded` means the applicable
            spend limit prevents the request. These typed funding refusals apply only when a payer
            exists. `unfunded` means no provider key and no credits, not merely zero available credits.
            `outside_funding_authority` means the matched route is outside the funding ceiling selected
            for this request. `not connected` means the provider is served by this deployment but no
            account is connected to this workspace; `not enabled` means the provider is not served by
            this deployment. `class_not_selected` means a matched sibling class lost the
            one-class-per-route selection while its route remains inside the authority ceiling; it
            excludes the class, not the route. `open_ended_unfit` means the provider can end a
            sandbox, so it cannot serve one with `lifetime_seconds: null`; `open_ended_needs_byok`
            means that request was made on a credit-funded route. Clients must tolerate reasons
            they do not know.
        gate:
          type: string
        knob:
          type: string
        resource:
          type: string
        extra:
          type: string
        limit:
          type: string
        detail:
          type:
            - string
            - "null"
        details:
          type: object
          description: |
            Structured facts for the refusal, such as available and required credits or the applicable
            spend limit.
          additionalProperties: true
        fix:
          type:
            - string
            - "null"
          description: Setup action for this failed check, when one is available.
        asked:
          description: What the request asked for, in the request's own form.
        offered:
          description: What the declaration offers, in the declaration's own form.
        narrowed_by:
          type:
            - string
            - "null"
          enum:
            - account
            - null
        between:
          type: array
          items:
            type: string
        value:
          description: The one value that caused the failure, when one did.
    Contract:
      type: object
      required:
        - spec_version
        - gates
        - resources
        - attested
      description: What this sandbox may use, fixed at creation. No gate is ever added to a running sandbox.
      properties:
        spec_version:
          type: string
          pattern: ^(boxspec|sandbox-provider-spec)/[0-9]+\.[0-9]+(-draft(\.[0-9]+)?)?$
        gates:
          type: object
          additionalProperties:
            $ref: "#/components/schemas/GrantedGate"
        resources:
          type: object
          additionalProperties: true
          description: What was provisioned, plus the adapter's answers for long_running and extendable_lifetime. A value may be "unknown" or null.
        attested:
          type: object
          description: Which resource values rest on a dated citation rather than on adapter code.
          additionalProperties:
            type: object
            required:
              - source
              - read
            properties:
              source:
                type: string
              read:
                type: string
                format: date
    GrantedGate:
      type: object
      required:
        - variant
        - basis
        - granted
        - also
        - settings
        - unavailable
      description: One gate as granted to this sandbox.
      properties:
        variant:
          type: integer
          minimum: 0
          description: Index into the declaration of route.adapter_version. Only meaningful with that version.
        basis:
          type: string
          enum:
            - implemented
            - proven
          description: The weakest basis among the granted values; for a gate granted with no named knob, the variant's own.
        granted:
          type: object
          additionalProperties: true
          description: |
            One entry per knob the request named: the granted values for a pick-one knob, the
            string "any" where the request said so, the boolean for a yes/no knob, the declared
            number for a number knob. This is what the code may rely on.
        also:
          type: object
          additionalProperties: true
          description: The variant's other answers, including declared limits, for information. Calls that depend on them are refused.
        settings:
          type: object
          additionalProperties: true
        unavailable:
          type:
            - string
            - "null"
          description: Set when the account has since lost the gate; that gate's calls then answer gate_unavailable.
    SandboxRequestSnapshot:
      type: object
      additionalProperties: true
      description: |
        The recorded creation request after server-side expansion and defaulting, without
        request-only secret values, environment values or labels. A sandbox can outlive the
        create-request schema that was current when it was made, so this historical snapshot
        accepts fields from earlier contract versions. Clients should interpret only fields
        they recognize and preserve unknown fields when displaying or storing the snapshot.
    HiddenSecretResult:
      type: object
      additionalProperties: false
      required:
        - placeholder
        - hosts
      properties:
        placeholder:
          type: string
          minLength: 1
          description: The safe token to place where this secret should be used.
        hosts:
          type: array
          uniqueItems: true
          items:
            allOf:
              - $ref: "#/components/schemas/SandboxProviderSpecHostname"
              - type: string
          description: Hosts to which the adapter sends this secret.
    TcpPortEndpoint:
      type: object
      additionalProperties: false
      required:
        - host
        - port
        - tls
      properties:
        host:
          type: string
          minLength: 1
        port:
          type: integer
          minimum: 1
          maximum: 65535
        tls:
          type: string
          enum:
            - none
            - terminated
    SandboxLimits:
      type: object
      properties:
        buffered_output_bytes:
          type: integer
          minimum: 1
          description: Maximum combined stdout/stderr for the buffered command endpoint.
        file_transfer_bytes:
          type: integer
          minimum: 1
          default: 1048576
          description: Maximum file size of `readFile` and `writeFile` on this route.
        upload_max_bytes:
          type: integer
          minimum: 0
          description: Per route, the largest file `createSandboxUpload` accepts.
        download_max_bytes:
          type: integer
          minimum: 0
          description: Per route, the largest file `createSandboxDownload` accepts.
        inline_transfer_max_bytes:
          type: integer
          minimum: 1
          description: |
            Largest file this route serves inline in one createSandboxDownload (send
            `inline_below`) or relays for upload, cheaper than its transfer link;
            absent when the route has none.
    VolumeAttachment:
      type: object
      additionalProperties: false
      required:
        - id
        - path
        - access
        - restored_checkpoint
        - restore_duration_ms
      properties:
        id:
          $ref: "#/components/schemas/VolumeId"
        path:
          type: string
        access:
          type: string
          enum:
            - write
            - read
            - shared
            - branch
        restored_checkpoint:
          type:
            - integer
            - "null"
          minimum: 1
        restore_duration_ms:
          type:
            - integer
            - "null"
          minimum: 0
    SandboxPage:
      type: object
      required:
        - data
        - next_cursor
        - counts
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/Sandbox"
        next_cursor:
          type:
            - string
            - "null"
        counts:
          $ref: "#/components/schemas/SandboxListCounts"
    SandboxListCounts:
      type: object
      required:
        - live
        - ended
        - by_state
      description: |
        Counts across all pages matching the other query filters. live and ended ignore
        lifecycle; by_state also ignores the state filter. Values are workspace scoped.
      properties:
        live:
          type: integer
          minimum: 0
        ended:
          type: integer
          minimum: 0
        by_state:
          type: object
          additionalProperties:
            type: integer
            minimum: 0
    MatchResult:
      type: object
      required:
        - spec_version
        - request
        - eligible
        - excluded
        - nearest
        - closest
      description: |
        What creating with the same body would do, without provisioning, reserving or
        recording anything. An empty eligible list is a 200 for ordinary capability exclusions.
        With a payer, typed funding refusals appear as exclusions when another funding candidate
        remains eligible; if every funding candidate is refused, the request returns the selected
        typed refusal as an error. A workspace with no provider key and no credits gets a 409
        no_eligible_route naming both remedies; a workspace with no candidate at all also gets a 409
        no_eligible_route. Matched classes omitted by the one-class-per-route selection appear in
        excluded.
      properties:
        spec_version:
          type: string
          pattern: ^(boxspec|sandbox-provider-spec)/[0-9]+\.[0-9]+(-draft(\.[0-9]+)?)?$
        request:
          allOf:
            - $ref: "#/components/schemas/CreateSandbox"
          description: The body as matched, with profiles expanded and defaults filled in.
        eligible:
          type: array
          description: The eligible candidates, in the order creation would try them.
          items:
            $ref: "#/components/schemas/EligibleRoute"
        excluded:
          type: array
          items:
            $ref: "#/components/schemas/ExcludedRoute"
        nearest:
          type: array
          description: |
            The smallest changes that would make a route eligible, fewest changes first, then
            most routes gained. A change maps a dotted path in the request to its new value,
            null meaning remove it.
          items:
            type: object
            required:
              - change
              - then_eligible
            properties:
              change:
                type: object
                additionalProperties: true
              then_eligible:
                type: array
                items:
                  type: string
        closest:
          description: The excluded candidate that failed the fewest checks, or null when none was excluded.
          oneOf:
            - $ref: "#/components/schemas/ExcludedRoute"
            - type: "null"
    EligibleRoute:
      type: object
      required:
        - provider
        - class
        - account_id
        - supply
        - adapter_version
        - gates
        - resources
        - attested
        - lifetime
      description: One candidate that fits, with the contract it would grant.
      properties:
        provider:
          type: string
        class:
          type:
            - string
            - "null"
        account_id:
          type:
            - string
            - "null"
        supply:
          type: string
        adapter_version:
          type: string
        gates:
          type: object
          additionalProperties:
            $ref: "#/components/schemas/GrantedGate"
        resources:
          type: object
          additionalProperties: true
        attested:
          type: object
          additionalProperties: true
        lifetime:
          $ref: "#/components/schemas/Lifetime"
        ranking:
          $ref: "#/components/schemas/EligibleRouteRanking"
        request_overrides:
          type: object
          additionalProperties: false
          minProperties: 1
          description: |
            The create request fields a caller must send to make this route eligible and priceable.
            Copy these fields into the create request and keep the caller's other fields. The object
            may select this route's funding authority, choose an image or lifetime, or name resources
            used to quote a managed route. Fields are never merged across routes.
          properties:
            route:
              $ref: "#/components/schemas/CreationRouteFilter"
              description: The funding authority that this route requires.
            image:
              type: string
              const: provider_default
              description: Use the image selected by this route's provider.
            lifetime_seconds:
              $ref: "#/components/schemas/lifetime_seconds"
              description: The lifetime the create request must send for this route.
            resources:
              $ref: "#/components/schemas/SandboxProviderSpecResources"
              description: The resource ask used to establish this route's eligibility and price.
    EligibleRouteRanking:
      type: object
      required:
        - position
        - reason
        - explanation
        - price
      description: |
        Why this route sits where it does in the order creation would try, with its hourly quote.
        It explains the ordering of this dry run; creation re-ranks with the signals of that moment.
        Absent on responses from a Cloud that predates it.
      properties:
        position:
          type: integer
          minimum: 1
          description: Place in the order creation would try, 1 first.
        reason:
          type: string
          enum:
            - lowest_price
            - weighted_price
            - lowest_time
            - time_unmeasured
            - predicted_completion
            - predicted_ready_time
            - explicit_order
            - after_named_providers
            - provider_key_order
            - only_eligible_route
            - health_deprioritized
            - price_unknown
            - no_signals
          x-boxrouter-open-enum: true
          x-boxrouter-choices:
            lowest_price:
              label: Lowest price
              line: Lowest hourly price of the routes ranked together.
            weighted_price:
              label: Weighted price
              line: Automatic mode leads with a draw weighted toward lower prices, so the leader can change between creates; the routes behind it follow by price.
            lowest_time:
              label: Lowest time
              line: Shortest run time measured on reference workloads first.
            time_unmeasured:
              label: Time unmeasured
              line: No current run time was measured for this provider on reference workloads, so it follows the measured routes, ordered by ready time, then price.
            predicted_completion:
              label: Predicted completion
              line: Soonest predicted completion among the routes ranked together.
            predicted_ready_time:
              label: Predicted ready time
              line: Ordered by predicted time to ready from this workspace's history.
            explicit_order:
              label: Explicit order
              line: Placed by the provider order you set in Routing or in the request.
            after_named_providers:
              label: After named providers
              line: Behind the providers you named in your order; the others keep BoxRouter's ranking.
            provider_key_order:
              label: Provider key order
              line: Another key of a provider already placed; one provider's keys are tried in the order you set in Routing.
            only_eligible_route:
              label: Only eligible route
              line: Nothing else to rank it against in its group.
            health_deprioritized:
              label: Health deprioritized
              line: Recent failures or degraded health put it behind healthier routes.
            price_unknown:
              label: Price unknown
              line: No price quote at this size, so routes with a quote are tried first.
            no_signals:
              label: No signals
              line: No price or time signals exist yet, so it keeps BoxRouter's default order.
          description: |
            Why the route ranks here. explicit_order is your own provider order; after_named_providers is a provider behind the ones you named; provider_key_order is
            a later key of a provider already placed; time_unmeasured means a time sort has no current
            measured run time for the route's provider; price_unknown means no comparable hourly quote
            is available. New values may appear.
        explanation:
          type: string
          description: The reason in one sentence; any price mention refers to the hourly quote in price.
        price:
          type: object
          required:
            - usd_per_hour
            - kind
            - basis
          description: The hourly quote at the size this route would grant. For BYOK, kind `list_price` is the provider list price paid directly to the provider; for managed supply, kind `customer_price` is BoxRouter's customer price charged to credits. This is not a final bill or a complete-task estimate.
          properties:
            usd_per_hour:
              type:
                - string
                - "null"
              pattern: ^(0|[1-9][0-9]*)(\.[0-9]+)?$
              description: Null when the size is unknown or the price version has no quote for it.
            kind:
              type: string
              enum:
                - list_price
                - customer_price
                - unknown
              x-boxrouter-open-enum: true
              description: Kind of hourly quote; unknown means no quote is available.
            basis:
              type: string
              description: The size priced and whose price it is, or why there is no price.
    lifetime_seconds:
      type:
        - integer
        - "null"
      minimum: 60
      description: |
        The sandbox's lifetime in seconds, starting when BoxRouter begins creation on the
        selected route. Creation time counts, so less time may remain when the sandbox is
        ready. The minimum is 60 seconds. The requested value is matched against the route's
        max_lifetime_seconds, and a credit-funded sandbox reserves credits for all of it.

        Leave it out for a credit-funded sandbox on a route that can extend a lifetime
        (`extendable_lifetime`) and the sandbox runs for as long as the workspace's credit
        balance covers it: credits are reserved one hour at a time and renewed before each
        hour ends. `funded_until` on the sandbox says when the current reservation ends; the
        sandbox ends then if the balance could not cover another hour. Left out on any other
        create (a route that cannot extend a lifetime, or a sandbox paid with the workspace's
        own provider account), it is 300 seconds.

        Null asks for a sandbox with no lifetime: it runs until you destroy it. Only a provider
        that never ends a sandbox serves it, and only on the workspace's own provider account;
        leave at_end out with it.
    RunCommand:
      type: object
      additionalProperties: false
      required:
        - command
      description: |
        The one command request body. output chooses how the output comes back; a body that
        leaves it out asks for text. output_limit_bytes bounds each stream in either form.
      properties:
        command:
          x-boxrouter-input-error: Command must not contain NUL bytes
          type: string
          pattern: ^[^\u0000]*$
          description: Bash source executed once, including an empty script. NUL characters are invalid. Must not appear in operational logs by default.
        env:
          x-boxrouter-input-error: Command env requires valid shell variable names and values without NUL
          type:
            - object
            - "null"
          propertyNames:
            pattern: ^[A-Za-z_][A-Za-z0-9_]*(?![\s\S])
          additionalProperties:
            type: string
            pattern: ^[^\u0000]*$
          description: Literal environment overrides for this command only, applied after shell initialization. Omission or null retains the provider environment. Not logged.
        cwd:
          x-boxrouter-input-error: Command cwd must be an absolute sandbox path without NUL bytes
          type:
            - string
            - "null"
          pattern: ^/[^\u0000]*$
          description: |
            Absolute sandbox working directory for this command only. Omitted, the command runs
            in the sandbox's default directory.
        timeout_seconds:
          x-boxrouter-input-error: Command timeout_seconds must be a positive integer
          type: integer
          minimum: 1
          default: 60
          description: |
            Execution limit in seconds. At the deadline the command's process group receives TERM,
            then KILL 5 seconds later; children that leave the process group are not ended. The
            call may return shortly after the deadline while it finishes collecting output, and
            answers 200 with timed_out true, exit_code null and the output written until then.
        output:
          type: string
          enum:
            - text
            - bytes
          default: text
          description: |
            How the result carries what the command wrote. text: stdout and stderr as strings,
            with invalid bytes replaced. bytes: stdout_base64 and stderr_base64 instead, the
            exact bytes, so a caller reads one pair or the other and never both (D26).
        output_limit_bytes:
          $ref: "#/components/schemas/OutputLimitBytes"
    OutputLimitBytes:
      type: integer
      minimum: 1
      maximum: 524288
      default: 524288
      description: |
        The most bytes returned from each of stdout and stderr: 512 KiB by default and at most,
        half the advertised limits.buffered_output_bytes (1 MiB) that both streams share. Output
        over the limit does not fail the command: the result carries the exit status and the first
        output_limit_bytes of that stream, with its truncated flag true. A value above the maximum
        is a validation error before any dispatch. Use a process for complete large output.
    CommandResult:
      type: object
      required:
        - stdout
        - stderr
        - exit_code
        - timed_out
        - stdout_truncated
        - stderr_truncated
      description: |
        What a command answers. A nonzero exit_code and a timeout are both a 200 with a result,
        never an HTTP failure; a client that wants an error raises it from the result (check).
      properties:
        stdout:
          type: string
        stderr:
          type: string
        exit_code:
          $ref: "#/components/schemas/ExitCode"
        timed_out:
          $ref: "#/components/schemas/TimedOut"
        stdout_truncated:
          $ref: "#/components/schemas/OutputTruncated"
        stderr_truncated:
          $ref: "#/components/schemas/OutputTruncated"
        native_error:
          type:
            - string
            - "null"
          description: |
            Provider-reported error for this command, unchanged, when the provider supplies
            one; otherwise null. It supplements exit_code without changing its meaning.
    ExitCode:
      type:
        - integer
        - "null"
      description: |
        The command's exit status. Null only when timed_out is true: a command that was ended at its
        deadline did not exit on its own, so it has no status.
    TimedOut:
      type: boolean
      description: |
        True when the command reached its timeout_seconds and BoxRouter ended its process group.
        The result then carries the output the command wrote before that point, and exit_code is
        null. False for every command that exited on its own, whatever its exit_code.
    OutputTruncated:
      type: boolean
      description: True when the stream wrote more than output_limit_bytes and only the first output_limit_bytes are returned.
    ByteCommandResult:
      type: object
      required:
        - stdout_base64
        - stderr_base64
        - exit_code
        - timed_out
        - stdout_truncated
        - stderr_truncated
        - native_error
      description: |
        What a command asked for bytes answers: the exact bytes it wrote, base64, in place of
        the strings. A nonzero exit_code and a timeout are still a 200.
      properties:
        stdout_base64:
          type: string
          contentEncoding: base64
        stderr_base64:
          type: string
          contentEncoding: base64
        exit_code:
          $ref: "#/components/schemas/ExitCode"
        timed_out:
          $ref: "#/components/schemas/TimedOut"
        stdout_truncated:
          $ref: "#/components/schemas/OutputTruncated"
        stderr_truncated:
          $ref: "#/components/schemas/OutputTruncated"
        native_error:
          type:
            - string
            - "null"
          description: |
            Always null in this form: a provider reports one against the text result, which is
            the only one that has it. Present so no field of a result is ever absent.
    CreateSandboxUpload:
      type: object
      additionalProperties: false
      required:
        - path
        - size
      properties:
        path:
          type: string
          minLength: 1
          pattern: ^/[^\u0000]*$
          description: Absolute path inside the sandbox, validated like `writeFile`.
        size:
          type: integer
          minimum: 0
          description: Exact file size in bytes. At most the route's `limits.upload_max_bytes`, otherwise `resource_limit` (413).
    SandboxUpload:
      type: object
      additionalProperties: false
      required:
        - id
        - sandbox_id
        - path
        - size
        - route
        - part_size
        - parts
        - expires_at
      description: |
        Upload session. Send every part as its `TransferPart` describes, then complete with each
        response `ETag`, if any. Part URLs may embed secret signatures: never log them. The links
        and this session last up to six hours after creation and end at `expires_at`. At launch
        there is exactly one part.
      properties:
        id:
          $ref: "#/components/schemas/SandboxUploadId"
        sandbox_id:
          type: string
          minLength: 1
        path:
          type: string
          minLength: 1
          pattern: ^/[^\u0000]*$
        size:
          type: integer
          minimum: 0
        route:
          $ref: "#/components/schemas/SandboxTransferRoute"
        part_size:
          type: integer
          minimum: 0
          description: Bytes per part except the last. Equals `size` for a single part, or 0 for an empty file.
        parts:
          type: array
          minItems: 1
          maxItems: 10000
          items:
            $ref: "#/components/schemas/TransferPart"
        expires_at:
          type: string
          format: date-time
          description: When the part links and this session end; at most six hours after creation.
    SandboxUploadId:
      type: string
      pattern: ^sup_[0-9a-f]{32}$
    SandboxTransferRoute:
      type: string
      enum:
        - direct
        - relay
      x-boxrouter-open-enum: true
      description: Informational. Which path the bytes take. Clients must not branch on it.
    TransferPart:
      type: object
      additionalProperties: false
      required:
        - number
        - size
        - url
      description: |
        One request a client makes to move part of a file. Volume upload parts are this shape with
        every default, so one client serves both. The response `ETag` header, if any, is
        passed back on completion; an absent `ETag` is passed as no `etag`.
      properties:
        number:
          type: integer
          minimum: 1
          maximum: 10000
        size:
          type: integer
          minimum: 0
          description: Exact byte count of this part.
        url:
          type: string
          format: uri
          description: |
            Where to send this part. The URL may embed a secret signature: clients must never log
            it, include it in error messages, or persist it. Treat it as opaque.
        method:
          type: string
          enum:
            - PUT
            - POST
          default: PUT
          description: HTTP method to use.
        headers:
          type: object
          additionalProperties:
            type: string
          default: {}
          description: Headers the client must send with this request, exactly as given.
        form_field:
          type: string
          minLength: 1
          description: |
            When present, send the bytes as `multipart/form-data` with a single file field of this
            name. When absent, send the raw bytes as the request body with `Content-Length` equal to
            `size`.
    CompleteSandboxUpload:
      type: object
      additionalProperties: false
      required:
        - parts
      properties:
        parts:
          type: array
          minItems: 1
          maxItems: 10000
          description: Each part number exactly once.
          items:
            type: object
            additionalProperties: false
            required:
              - number
            properties:
              number:
                type: integer
                minimum: 1
                maximum: 10000
              etag:
                type: string
                minLength: 1
                description: The response `ETag` header for this part. Omit when the response had none.
    CompletedSandboxUpload:
      type: object
      additionalProperties: false
      required:
        - path
        - size
      properties:
        path:
          type: string
          minLength: 1
          pattern: ^/[^\u0000]*$
        size:
          type: integer
          minimum: 0
    CreateSandboxDownload:
      type: object
      additionalProperties: false
      required:
        - path
      properties:
        path:
          type: string
          minLength: 1
          pattern: ^/[^\u0000]*$
          description: Absolute path of an existing file inside the sandbox.
        inline_below:
          type: integer
          minimum: 0
          description: "Optional byte floor, chosen by the caller: when the provider reports the file's size below this value the response's `content` carries the body inline instead of creating a link. At or above it a link is created as usual — a provider-direct one where the route supports it."
    SandboxDownload:
      type: object
      additionalProperties: false
      required:
        - path
        - size
      description: "Download answer: when `content` is present it carries the file body inline (base64) and no link exists; otherwise `url` and `expires_at` give a temporary link that lasts up to one hour after creation. The URL may embed a secret signature, so never log it."
      properties:
        path:
          type: string
          minLength: 1
          pattern: ^/[^\u0000]*$
        size:
          type:
            - integer
            - "null"
          minimum: 0
          description: File size in bytes when the provider reports it; null when it is not known until the download's response. A complete download ends exactly at its response's Content-Length.
        content:
          type: string
          description: The file body, base64-encoded, when `inline_below` admitted it. Absent when a link was created instead.
        route:
          $ref: "#/components/schemas/SandboxTransferRoute"
        url:
          type: string
          format: uri
          description: Temporary download link; absent when `content` carries the body.
        method:
          type: string
          enum:
            - GET
          default: GET
        headers:
          type: object
          additionalProperties:
            type: string
          default: {}
        expires_at:
          type: string
          format: date-time
          description: When the link ends; at most one hour after creation. Absent when `content` carries the body.
    DirectoryEntry:
      type: object
      additionalProperties: false
      required:
        - name
        - type
      properties:
        name:
          type: string
          minLength: 1
          description: The entry's name within the directory, without a path.
        type:
          type: string
          enum:
            - file
            - directory
            - symlink
            - other
          description: What the entry itself is. A symbolic link is symlink whether or not its target exists; it is never followed.
    CreateDirectoryRequest:
      type: object
      additionalProperties: false
      required:
        - path
      properties:
        path:
          type: string
          minLength: 1
          pattern: ^/[^\u0000]*$
        parents:
          type: boolean
          default: true
    RemovePathRequest:
      type: object
      additionalProperties: false
      required:
        - path
      properties:
        path:
          type: string
          minLength: 1
          pattern: ^/[^\u0000]*$
        recursive:
          type: boolean
          default: false
    PriceVersionPage:
      type: object
      required:
        - data
      properties:
        data:
          type: array
          description: |
            Live price versions, newest `effective_from` first. Not paginated: there are a few
            versions per provider. Empty when nothing matches, including an unknown `provider`.
          items:
            $ref: "#/components/schemas/PriceVersion"
    PriceVersion:
      type: object
      required:
        - provider
        - version
        - effective_from
        - effective_to
        - last_confirmed_at
        - document
      properties:
        provider:
          type: string
          pattern: ^[a-z][a-z0-9-]*$
          description: Provider ID, for example `e2b`.
        version:
          type: string
          pattern: ^[a-z][a-z0-9-]*@[0-9]{4}-[0-9]{2}-[0-9]{2}(-[0-9]{6}Z)?$
          description: The ID every cost names, for example `e2b@2026-10-01`, or `modal@2026-10-03-130908Z` when a provider has several versions in one day.
        effective_from:
          type: string
          format: date-time
          description: When this version's prices start applying.
        effective_to:
          type:
            - string
            - "null"
          format: date-time
          description: When a later version took over; null for the version in effect now.
        last_confirmed_at:
          type: string
          format: date-time
          description: When BoxRouter last read the provider's pricing page and found these prices unchanged.
        document:
          $ref: "#/components/schemas/PriceVersionDocument"
          description: |
            The price version itself: unit rates, meter rules, the pages they were read from, with
            verbatim quotes, exclusions and open questions. Its structure follows the
            price-version schema included in this API contract.
            Never changes once the version is live; a correction is a new version.
    PriceVersionDocument:
      title: Price version
      description: 'What one provider charged from one date: unit rates, how each is metered, the pages they were read from, what the list leaves out and what is still unknown. Rates are decimal strings, never JSON numbers. A rate that is absent was not published, which is not the same as free: a published free rate is `"0"`. Stored status, effective_to, first_seen_at, last_confirmed_at and content_hash belong to the database, not to this document.'
      type: object
      additionalProperties: false
      required:
        - provider
        - version
        - effective_from
        - retrieved_at
        - sources
        - classes
        - exclusions
        - open_questions
      properties:
        provider:
          type: string
          pattern: ^[a-z][a-z0-9-]*$
        version:
          description: "The id every cost names: the provider, an @, and the UTC date the version was read, for example e2b@2026-10-20-061802Z. If a provider gets more than one version on one day, append the UTC time as HHMMSSZ, for example modal@2026-10-20-061900Z. The provider part equals `provider`."
          type: string
          pattern: ^[a-z][a-z0-9-]*@[0-9]{4}-[0-9]{2}-[0-9]{2}(?:-([01][0-9]|2[0-3])[0-5][0-9][0-5][0-9]Z)?$
        effective_from:
          description: "When this price starts applying: the provider's announced date if it gave one, else when it was first seen."
          $ref: "#/components/schemas/PricingTimestamp"
        retrieved_at:
          description: When the pages below were read.
          $ref: "#/components/schemas/PricingTimestamp"
        boundary:
          description: "Legacy: where billing starts and ends, kept so documents published before 0.2 stay byte-identical. Code ignores it; the provider's billing.yaml is the source."
          $ref: "#/components/schemas/PriceVersionDocumentBoundary"
        supersedes:
          description: Earlier price version ids corrected by this document. Selectors ignore these versions when the bundled price files include this document.
          type: array
          uniqueItems: true
          items:
            $ref: "#/components/schemas/PriceVersionDocumentVersion"
        month_hours:
          description: The price version's hours per calendar month for monthly rates, such as 730 for 365/12 days.
          $ref: "#/components/schemas/PriceVersionDocumentPositiveDecimal"
        snapshot_storage:
          description: Independent retained snapshot storage price; absent means unpriced. A disk rate proxy must say so explicitly.
          type: object
          additionalProperties: false
          required:
            - usd
            - per
            - rate_source
            - source_url
            - checked_at
          properties:
            usd:
              $ref: "#/components/schemas/PriceVersionDocumentPositiveDecimal"
            per:
              type: string
              enum:
                - gb-month
                - gib-month
                - gib-hour
              x-boxrouter-open-enum: true
            rate_source:
              enum:
                - published_snapshot_rate
                - published_disk_rate_proxy
            source_url:
              type: string
              pattern: ^https?://[^\s]+$
            checked_at:
              $ref: "#/components/schemas/PricingTimestamp"
            note:
              type: string
              minLength: 1
        image_storage:
          description: "Independent price of storing a deployment image the provider keeps for fast, repeatable creation; absent means unpriced. Not a sandbox's own cost: no usage record carries image size."
          type: object
          additionalProperties: false
          required:
            - usd
            - per
            - rate_source
            - source_url
            - checked_at
          properties:
            usd:
              $ref: "#/components/schemas/PriceVersionDocumentPositiveDecimal"
            per:
              type: string
              enum:
                - gb-month
                - gib-month
              x-boxrouter-open-enum: true
            rate_source:
              const: published_image_rate
            source_url:
              type: string
              pattern: ^https?://[^\s]+$
            checked_at:
              $ref: "#/components/schemas/PricingTimestamp"
            note:
              type: string
              minLength: 1
        volume_storage:
          description: "Independent price of a provider volume, billed on its provisioned size for as long as it exists; absent means unpriced. Not a sandbox's own cost: no usage record carries volume size."
          type: object
          additionalProperties: false
          required:
            - usd
            - per
            - rate_source
            - source_url
            - checked_at
          properties:
            usd:
              $ref: "#/components/schemas/PriceVersionDocumentPositiveDecimal"
            per:
              type: string
              enum:
                - gb-month
                - gib-month
              x-boxrouter-open-enum: true
            rate_source:
              const: published_volume_rate
            source_url:
              type: string
              pattern: ^https?://[^\s]+$
            checked_at:
              $ref: "#/components/schemas/PricingTimestamp"
            included_gb:
              description: "Volume storage the provider includes before the rate applies, in included_unit (Modal: 1 TiB a month, written 1024 GiB)."
              $ref: "#/components/schemas/PriceVersionDocumentPositiveDecimal"
            included_unit:
              const: GiB
            note:
              type: string
              minLength: 1
          dependentRequired:
            included_gb:
              - included_unit
            included_unit:
              - included_gb
        sources:
          description: The pages the rates and rules were read from, each with a verbatim quote.
          type: array
          minItems: 1
          items:
            type: object
            additionalProperties: false
            required:
              - url
              - quote
            properties:
              url:
                type: string
                pattern: ^https?://[^\s]+$
              quote:
                type: string
                minLength: 1
        classes:
          description: One rate set per class or runtime the provider prices differently (Modal's gVisor and VM, a Daytona class). Exactly one is the default, which applies when a usage record names no class. Class names are unique.
          type: array
          minItems: 1
          items:
            $ref: "#/components/schemas/PriceVersionDocumentClass"
          contains:
            type: object
            required:
              - default
          minContains: 1
          maxContains: 1
        exclusions:
          description: "Charges outside a sandbox's own cost that the rates do not cover: plan fees, concurrency add-ons, credits, storage that is not priced from a sandbox's size."
          type: array
          items:
            type: string
            minLength: 1
        coverage:
          description: "Every billable charge of a managed sandbox, one entry per kind, each stated as `in_quote`, `not_billed` or `unbounded`. `reservation_quote` returns `bounded` only when every kind is stated and none of cpu, memory or disk is `unbounded`; a missing kind makes the quote `unknown`, and so does a version without this block. Kinds: cpu, memory, disk, creation, egress, snapshot_storage, image_storage, volume_storage, requests (per-request or per-use charges such as starts, proxies and drive reads)."
          type: object
          additionalProperties: false
          properties:
            cpu:
              $ref: "#/components/schemas/PriceVersionDocumentCoverageEntry"
            memory:
              $ref: "#/components/schemas/PriceVersionDocumentCoverageEntry"
            disk:
              $ref: "#/components/schemas/PriceVersionDocumentCoverageEntry"
            creation:
              $ref: "#/components/schemas/PriceVersionDocumentCoverageEntry"
            egress:
              $ref: "#/components/schemas/PriceVersionDocumentCoverageEntry"
            snapshot_storage:
              $ref: "#/components/schemas/PriceVersionDocumentCoverageEntry"
            image_storage:
              $ref: "#/components/schemas/PriceVersionDocumentCoverageEntry"
            volume_storage:
              $ref: "#/components/schemas/PriceVersionDocumentCoverageEntry"
            requests:
              $ref: "#/components/schemas/PriceVersionDocumentCoverageEntry"
        open_questions:
          description: What the pages leave unclear, so a reader knows which rule is an assumption.
          type: array
          items:
            type: string
            minLength: 1
    PriceVersionDocumentBoundary:
      type: object
      additionalProperties: false
      required:
        - start
        - end
        - checked
      properties:
        start:
          enum:
            - create_sent
            - create_returned
            - destroy_sent
            - destroy_returned
            - provider
            - unknown
        end:
          enum:
            - create_sent
            - create_returned
            - destroy_sent
            - destroy_returned
            - provider
            - unknown
        checked:
          type: boolean
        evidence:
          type:
            - string
            - "null"
          minLength: 1
      allOf:
        - if:
            properties:
              checked:
                const: true
            required:
              - checked
          then:
            required:
              - evidence
            properties:
              evidence:
                type: string
                minLength: 1
    PriceVersionDocumentVersion:
      description: "The id every cost names: the provider, an @, and the UTC date the version was read, for example e2b@2026-10-20-061802Z. If a provider gets more than one version on one day, append the UTC time as HHMMSSZ, for example modal@2026-10-20-061900Z. The provider part equals `provider`."
      type: string
      pattern: ^[a-z][a-z0-9-]*@[0-9]{4}-[0-9]{2}-[0-9]{2}(?:-([01][0-9]|2[0-3])[0-5][0-9][0-5][0-9]Z)?$
    PriceVersionDocumentPositiveDecimal:
      description: A decimal string greater than zero.
      type: string
      pattern: ^(0\.[0-9]*[1-9][0-9]*|[1-9][0-9]*(\.[0-9]+)?)$
    PriceVersionDocumentClass:
      type: object
      additionalProperties: false
      required:
        - name
        - rates
        - meter
      properties:
        name:
          type: string
          pattern: ^[a-z0-9][a-z0-9._-]*$
        default:
          description: Present, and true, on the default class only.
          const: true
        covers:
          description: The spec classes this rate set prices (Modal's default covers default and vm; Daytona's on-demand covers container and vm). A usage record's class names a spec class. Absent, the class covers only the spec class of its own name. No spec class is covered by two classes of one version.
          type: array
          minItems: 1
          uniqueItems: true
          items:
            type: string
            pattern: ^[a-z0-9][a-z0-9._-]*$
        derived_from:
          description: Rates this class takes from another class of the same version times a factor the provider publishes, where it publishes no unit rate of its own (Agent37's VM compute is 2.5x its default rate). Each named rate equals that class's rate times the factor, and a source quote states the factor.
          type: object
          additionalProperties: false
          required:
            - class
            - factor
            - rates
          properties:
            class:
              type: string
              pattern: ^[a-z0-9][a-z0-9._-]*$
            factor:
              $ref: "#/components/schemas/PriceVersionDocumentPositiveDecimal"
            rates:
              type: array
              minItems: 1
              uniqueItems: true
              items:
                type: string
        rates:
          $ref: "#/components/schemas/PriceVersionDocumentRates"
        meter:
          $ref: "#/components/schemas/PriceVersionDocumentMeter"
        region_multipliers:
          description: A multiplier on every rate of this class for sandboxes in the named region (Modal's 1.15 for a broad region). A region that is not listed is charged at the base rate.
          type: object
          minProperties: 1
          propertyNames:
            pattern: ^[a-z0-9][a-z0-9._-]*$
          additionalProperties:
            $ref: "#/components/schemas/PriceVersionDocumentPositiveDecimal"
    PriceVersionDocumentRates:
      description: Unit rates, each optional. A rate is a price and the unit it is a price per, as the provider publishes it.
      type: object
      additionalProperties: false
      minProperties: 1
      properties:
        cpu:
          $ref: "#/components/schemas/PriceVersionDocumentCpuRate"
        memory:
          $ref: "#/components/schemas/PriceVersionDocumentGibRate"
        disk:
          $ref: "#/components/schemas/PriceVersionDocumentGibRate"
        gpu:
          description: Rates by GPU model, keyed by gpu-registry identifiers.
          $ref: "#/components/schemas/PriceVersionDocumentGpuRates"
        machine:
          $ref: "#/components/schemas/PriceVersionDocumentMachineRates"
        creation:
          $ref: "#/components/schemas/PriceVersionDocumentCreationRate"
        egress:
          $ref: "#/components/schemas/PriceVersionDocumentEgressRate"
        snapshot_storage:
          $ref: "#/components/schemas/PriceVersionDocumentStorageRate"
    PriceVersionDocumentCpuRate:
      type: object
      additionalProperties: false
      required:
        - usd
        - per
      properties:
        usd:
          $ref: "#/components/schemas/PricingDecimal"
        per:
          type: string
          description: A month is 730 hours, as providers that price per month define it.
          enum:
            - vcpu-second
            - vcpu-hour
            - vcpu-month
            - core-second
            - core-hour
          x-boxrouter-open-enum: true
        size_per_unit:
          description: vCPUs one core covers. Required for a core unit, which is not a vCPU (Modal's physical core), and absent for a vCPU unit.
          $ref: "#/components/schemas/PriceVersionDocumentPositiveDecimal"
      if:
        properties:
          per:
            enum:
              - core-second
              - core-hour
        required:
          - per
      then:
        required:
          - size_per_unit
      else:
        not:
          required:
            - size_per_unit
    PriceVersionDocumentGibRate:
      type: object
      additionalProperties: false
      required:
        - usd
        - per
      properties:
        usd:
          $ref: "#/components/schemas/PricingDecimal"
        per:
          type: string
          description: A month is 730 hours, as providers that price per month define it.
          enum:
            - gib-second
            - gib-hour
            - gib-month
            - gb-second
            - gb-hour
            - gb-month
          x-boxrouter-open-enum: true
        size_per_unit:
          description: "GiB one GB covers: 1 where the provider's GB is read as a GiB, as Vercel's and Blaxel's examples do. Required for a GB unit and absent for a GiB unit."
          $ref: "#/components/schemas/PriceVersionDocumentPositiveDecimal"
      if:
        properties:
          per:
            enum:
              - gb-second
              - gb-hour
              - gb-month
        required:
          - per
      then:
        required:
          - size_per_unit
      else:
        not:
          required:
            - size_per_unit
    PriceVersionDocumentGpuRates:
      type: object
      minProperties: 1
      propertyNames:
        pattern: ^[a-z][a-z0-9-]*:[a-z0-9][a-z0-9-]*$
      additionalProperties:
        $ref: "#/components/schemas/PriceVersionDocumentGpuRate"
    PriceVersionDocumentGpuRate:
      type: object
      additionalProperties: false
      required:
        - usd
        - per
      properties:
        usd:
          $ref: "#/components/schemas/PricingDecimal"
        per:
          type: string
          enum:
            - gpu-second
            - gpu-hour
          x-boxrouter-open-enum: true
    PriceVersionDocumentMachineRates:
      description: "Flat per-size machine rates, keyed by the provider's own size name. Each entry prices one published bundle: a sandbox whose size matches the entry's cpu and memory_gib is billed the flat rate while the meter runs it. A sandbox size that matches no entry has no machine price. Used where the provider sells a bundle with no published per-unit split."
      type: object
      minProperties: 1
      propertyNames:
        pattern: ^[a-z0-9][a-z0-9._-]*$
      additionalProperties:
        $ref: "#/components/schemas/PriceVersionDocumentMachineRate"
    PriceVersionDocumentMachineRate:
      type: object
      additionalProperties: false
      required:
        - usd
        - per
        - cpu
        - memory_gib
      properties:
        usd:
          $ref: "#/components/schemas/PricingDecimal"
        per:
          type: string
          enum:
            - machine-second
            - machine-hour
          x-boxrouter-open-enum: true
        cpu:
          description: vCPUs the published size gives.
          $ref: "#/components/schemas/PriceVersionDocumentPositiveDecimal"
        memory_gib:
          description: GiB of memory the published size gives; where the provider quotes GB it is read as a GiB.
          $ref: "#/components/schemas/PriceVersionDocumentPositiveDecimal"
        note:
          type: string
          minLength: 1
    PriceVersionDocumentCreationRate:
      type: object
      additionalProperties: false
      required:
        - usd
        - per
      properties:
        usd:
          $ref: "#/components/schemas/PricingDecimal"
        per:
          type: string
          enum:
            - creation
            - million-creations
          x-boxrouter-open-enum: true
    PriceVersionDocumentEgressRate:
      type: object
      additionalProperties: false
      required:
        - usd
        - per
      properties:
        usd:
          $ref: "#/components/schemas/PricingDecimal"
        per:
          type: string
          description: gib is 2^30 bytes; gb is 10^9 bytes.
          enum:
            - gib
            - gb
          x-boxrouter-open-enum: true
    PriceVersionDocumentStorageRate:
      type: object
      additionalProperties: false
      required:
        - usd
        - per
      properties:
        usd:
          $ref: "#/components/schemas/PricingDecimal"
        per:
          const: gb-month
    PriceVersionDocumentMeter:
      description: How each time-billed resource is metered. A resource with a rate and no meter rule cannot be priced.
      type: object
      additionalProperties: false
      patternProperties:
        ^(cpu|gpu|machine)$:
          $ref: "#/components/schemas/PriceVersionDocumentTimeMeter"
        ^memory$:
          $ref: "#/components/schemas/PriceVersionDocumentMemoryMeter"
        ^disk$:
          $ref: "#/components/schemas/PriceVersionDocumentDiskMeter"
        ^snapshot_storage$:
          $ref: "#/components/schemas/PriceVersionDocumentStorageMeter"
    PriceVersionDocumentTimeMeter:
      type: object
      additionalProperties: false
      required:
        - basis
        - billed_states
      properties:
        basis:
          description: "allocated: the size the sandbox has. active: only while the resource is in use (Vercel's CPU). larger_of_request_and_use: whichever is greater (Modal)."
          enum:
            - allocated
            - active
            - larger_of_request_and_use
        billed_states:
          description: The sandbox states in which this resource is charged.
          type: array
          minItems: 1
          uniqueItems: true
          items:
            enum:
              - running
              - paused
              - stopped
        minimum_seconds:
          description: The least that is charged for each period in a billed state.
          type: integer
          minimum: 1
        increment_seconds:
          description: Each period in a billed state is rounded up to a multiple of this.
          type: integer
          minimum: 1
        free_quantity:
          description: An allowance that is free for every sandbox, in the resource's size unit (vCPUs, GiB, GPUs). An allowance shared across an account is an exclusion, not a meter rule.
          $ref: "#/components/schemas/PricingDecimal"
    PriceVersionDocumentMemoryMeter:
      type: object
      additionalProperties: false
      required:
        - basis
        - billed_states
      properties:
        basis:
          description: "allocated: the size the sandbox has; active: only while the resource is in use; larger_of_request_and_use: whichever is greater."
          enum:
            - allocated
            - active
            - larger_of_request_and_use
        billed_states:
          type: array
          minItems: 1
          uniqueItems: true
          items:
            enum:
              - running
              - paused
              - stopped
        minimum_seconds:
          type: integer
          minimum: 1
        increment_seconds:
          type: integer
          minimum: 1
        free_quantity:
          $ref: "#/components/schemas/PricingDecimal"
        memory_steps_mib:
          description: Discrete billed memory sizes in MiB. A request rounds up to the next listed size; a request above every tier has no price.
          type: array
          minItems: 1
          uniqueItems: true
          items:
            type: integer
            minimum: 1
    PriceVersionDocumentDiskMeter:
      type: object
      additionalProperties: false
      required:
        - basis
        - billed_states
      properties:
        basis:
          enum:
            - allocated
            - active
            - larger_of_request_and_use
        billed_states:
          type: array
          minItems: 1
          uniqueItems: true
          items:
            enum:
              - running
              - paused
              - stopped
        minimum_seconds:
          type: integer
          minimum: 1
        increment_seconds:
          type: integer
          minimum: 1
        free_quantity:
          $ref: "#/components/schemas/PricingDecimal"
        disk_gib_per_memory_gib:
          description: "Set where the provider charges disk by raising the memory it bills: how many GiB of disk count as one GiB of memory (Modal's 20). The class then has no disk rate."
          $ref: "#/components/schemas/PriceVersionDocumentPositiveDecimal"
    PriceVersionDocumentStorageMeter:
      type: object
      additionalProperties: false
      required:
        - billed_states
      properties:
        billed_states:
          type: array
          minItems: 1
          uniqueItems: true
          items:
            enum:
              - running
              - paused
              - stopped
    PriceVersionDocumentCoverageEntry:
      type: object
      additionalProperties: false
      required:
        - treatment
        - source
        - note
      properties:
        treatment:
          description: "`in_quote`: priced into a reservation quote (time-bounded, or the creation fee). `not_billed`: the provider does not bill it on managed sandboxes. `unbounded`: billable or possibly billable, but no ceiling follows from a size and a duration (egress, retained storage, request- or use-based); it is outside the quote and settled from the provider's own cost."
          enum:
            - in_quote
            - not_billed
            - unbounded
        source:
          description: "Where the treatment was read: a URL listed in `sources`, or a repo-relative path to committed evidence under `providers/`."
          type: string
          minLength: 1
        note:
          description: "The reason, in one sentence. For a charge the provider does not publish, say so: unpublished is never free."
          type: string
          minLength: 1
    ProviderPage:
      type: object
      required:
        - data
      description: |
        The routes this workspace can use as written or with the listed `request_overrides`, one
        entry per connected account or managed supply. Each entry has the gates, resources, attested
        facts and lifetime that `match` reports for the route, from the same adapter declarations.
        Overrides are create fields to add for that route; they are never merged across routes.
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/EligibleRoute"
  responses:
    Error:
      description: |
        Structured error. `retry` says whether to send the call again (`now`, `after_fix`, `inspect` or `never`) and `retry_after_seconds` how long to wait first. Unowned and unknown resource IDs both return 404. Admission overload returns 503 with code overloaded, outcome not_started and Retry-After. It reserves no operation and dispatches no provider work. Shared workspace/provider admission budgets return 429 with code rate_limited, outcome not_started and Retry-After. An incomplete request body can return 408 request_timeout before dispatch. Durable-state failures return 503 database_unavailable with outcome unknown; do not infer that execution did not occur. A read (GET or HEAD) that meets the same failure changed nothing: its message says the state is temporarily unavailable, and it is safe to repeat. A provider that cannot be reached when a create is prepared returns 502 provider_error with outcome not_started; nothing was created, and the create can be retried with the same Idempotency-Key or sent to another provider with `providers`.
        Native errors are sanitized; upstream credentials, request bodies and
        another workspace's metadata are never included. Server-Timing is present only when the response reached Dispatcher or held-response preparation; an earlier response, including pre-Dispatcher admission overload, may omit it.
      headers:
        X-Request-ID:
          $ref: "#/components/headers/RequestId"
        Server-Timing:
          $ref: "#/components/headers/ServerTiming"
        Retry-After:
          description: Seconds before retrying an eligible read; not permission to replay a mutation.
          schema:
            type: integer
            minimum: 0
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ApiErrorResponse"
  headers:
    RequestId:
      description: Server-generated correlation identifier; contains no credentials or payloads.
      schema:
        type: string
    ServerTiming:
      description: |
        Durations of server-side phases, for diagnosing slow requests. Phase names are not stable
        and may change.
      schema:
        type: string
      x-boxrouter-server-timing:
        version: 1
        unit: ms
        serviceAggregateMetric: service
        maxHeaderBytes: 8192
        maxOperationRows: 32
        decimalPlaces: 3
        safeNamePattern: ^[A-Za-z0-9_.:-]{1,64}$
        aggregates:
          - metric: service
            fieldStem: cloud
          - metric: database
            fieldStem: database
          - metric: code
            fieldStem: code
          - metric: provider_connect
            fieldStem: provider_connect
          - metric: provider_wait
            fieldStem: provider_wait
          - metric: accept
            fieldStem: accept
          - metric: queue
            fieldStem: queue
          - metric: admission
            fieldStem: admission
          - metric: encode
            fieldStem: encode
        aggregateGrammar:
          duration:
            parameter: dur
            type: number
            finite: true
            minimum: 0
          descriptionParameter: desc
          unknownDescription: unknown
          missingMeans: unreported
        operationGrammar:
          metric: op
          nameParameter: desc
          namePattern: safeNamePattern
          start:
            parameter: start
            type: number
            finite: true
            sign: signed
            relativeTo: recorderWindow
          duration:
            parameter: dur
            type: number
            finite: true
            minimum: 0
          outcomeParameter: outcome
          outcomes:
            - returned
            - raised
          requiresAggregateDependencyUnion: false
        omissionGrammar:
          metric: op-omitted
          count:
            parameter: desc
            type: integer
            minimum: 0
          duration:
            parameter: dur
            type: number
            finite: true
            minimum: 0
            sumOf: knownOmittedMeasuredRows
          requiresAggregateDependencyUnion: false
        coverageGrammar:
          metric: op-coverage
          descriptionParameter: desc
          unknownValue: unknown
          absentState: unreported
          states:
            - unknown
            - unreported
          absenceImpliesCompleteness: false
  parameters:
    LaunchIdempotencyKey:
      name: Idempotency-Key
      in: header
      required: true
      description: Unique identifier for this logical launch. Reuse it with the identical body to reconcile an uncertain response.
      schema:
        type: string
        minLength: 1
        maxLength: 128
        pattern: ^[^\u0000]+$
    Cursor:
      name: cursor
      in: query
      description: Opaque cursor from the preceding page; scoped to the original workspace and query.
      schema:
        type: string
        minLength: 1
        maxLength: 128
        pattern: ^[^\u0000]*$
    Limit:
      name: limit
      in: query
      description: Maximum number of records to return.
      schema:
        type: integer
        minimum: 1
        maximum: 100
        default: 20
    InstallationId:
      name: installation_id
      in: path
      required: true
      description: Stable GitHub App installation ID, represented as a string to avoid integer-width assumptions.
      schema:
        type: string
        minLength: 1
        maxLength: 128
        pattern: ^[^\u0000]*$
    Csrf:
      name: X-CSRF-Token
      in: header
      required: true
      schema:
        type: string
      description: Session-bound CSRF token; a matching configured Origin header is also required.
    IdempotencyKey:
      name: Idempotency-Key
      in: header
      description: Optional for this operation. Preserve the same stable identifier when retrying an uncertain response; never reuse it for new work. If omitted where allowed, Cloud generates a request-scoped key that cannot reconcile a later retry. Do not include credentials or payloads.
      schema:
        $ref: "#/components/schemas/IdempotencyKeyValue"
    InstallationQuery:
      name: installation_id
      in: query
      required: true
      description: Workspace-linked GitHub App installation whose repository access is being listed.
      schema:
        type: string
        minLength: 1
        maxLength: 128
        pattern: ^[^\u0000]*$
    RepositoryId:
      name: repository_id
      in: path
      required: true
      description: Stable GitHub repository ID bound to this workspace; owner/name is not an identifier.
      schema:
        type: string
        minLength: 1
        maxLength: 128
        pattern: ^[^\u0000]*$
    CsrfBrowserOnly:
      name: X-CSRF-Token
      in: header
      required: false
      schema:
        type: string
      description: Session-bound CSRF token. Required for browser-session calls, together with a matching configured Origin header; omit it when calling with an API key.
    PolicyId:
      name: policy_id
      in: path
      required: true
      description: Workspace-owned Actions policy ID.
      schema:
        type: string
        minLength: 1
        maxLength: 128
        pattern: ^[^\u0000]*$
    IfMatch:
      name: If-Match
      in: header
      required: true
      description: Strong ETag returned by the latest read; a stale value conflicts without changing the resource.
      schema:
        type: string
        minLength: 1
        maxLength: 256
        pattern: ^[^\u0000]*$
    RunId:
      name: run_id
      in: path
      required: true
      description: Workspace-owned BoxRouter workflow-run projection ID, not a GitHub run ID.
      schema:
        type: string
        minLength: 1
        maxLength: 128
        pattern: ^[^\u0000]*$
    JobId:
      name: job_id
      in: path
      required: true
      description: Workspace-owned BoxRouter job projection ID, not a GitHub job ID.
      schema:
        type: string
        minLength: 1
        maxLength: 128
        pattern: ^[^\u0000]*$
    PlacementId:
      name: placement_id
      in: path
      required: true
      description: Workspace-owned BoxRouter placement ID.
      schema:
        type: string
        minLength: 1
        maxLength: 128
        pattern: ^[^\u0000]*$
    SandboxId:
      name: sandbox_id
      in: path
      required: true
      description: Workspace-owned BoxRouter sandbox ID, not an upstream ID.
      schema:
        type: string
        minLength: 1
        pattern: ^[^\u0000]*$
    ProcessId:
      name: process_id
      in: path
      required: true
      description: Durable BoxRouter process ID owned by the addressed sandbox and workspace.
      schema:
        type: string
        minLength: 1
    RequiredIdempotencyKey:
      name: Idempotency-Key
      in: header
      required: true
      description: Required caller-generated identifier for one logical mutation. Preserve it when retrying an uncertain response; never reuse it for new work. Do not include credentials or payloads.
      schema:
        $ref: "#/components/schemas/IdempotencyKeyValue"
    TerminalId:
      name: terminal_id
      in: path
      required: true
      description: BoxRouter terminal ID owned by the addressed sandbox and workspace.
      schema:
        type: string
        minLength: 1
    PreviewId:
      name: preview_id
      in: path
      required: true
      description: Workspace-owned BoxRouter preview ID, as the open returned it.
      schema:
        type: string
        minLength: 1
        maxLength: 128
        pattern: ^[^\u0000]*$
    AccountId:
      name: account_id
      in: path
      required: true
      description: BoxRouter connected-account ID, scoped to this workspace.
      schema:
        type: string
        minLength: 1
        pattern: ^[^\u0000]*$
    VolumeId:
      name: volume_id
      in: path
      required: true
      schema:
        $ref: "#/components/schemas/VolumeId"
    UploadId:
      name: upload_id
      in: path
      required: true
      description: ID of the open volume file upload.
      schema:
        $ref: "#/components/schemas/UploadId"
    TransferIdempotencyKey:
      name: Idempotency-Key
      in: header
      required: true
      description: Unique identifier for this one file transfer call. Reuse it with the identical request to reconcile an uncertain response; never reuse it for new work.
      schema:
        $ref: "#/components/schemas/IdempotencyKeyValue"
    SandboxUploadId:
      name: upload_id
      in: path
      required: true
      description: ID of the open sandbox file upload.
      schema:
        $ref: "#/components/schemas/SandboxUploadId"
    Path:
      name: path
      in: query
      required: true
      description: Absolute path inside the sandbox, encoded as a query value.
      schema:
        type: string
        minLength: 1
        pattern: ^/[^\u0000]*$
