> ## Documentation Index
> Fetch the complete documentation index at: https://boxrouter.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Explain Routing without Creating

> 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.




## OpenAPI

````yaml openapi.json POST /sandboxes/match
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:
  /sandboxes/match:
    post:
      tags:
        - sandboxes
      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.
      operationId: matchSandbox
      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'
components:
  schemas:
    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.
    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'
    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
    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.
    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'
    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.
    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.
    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.
    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.
    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.
    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
    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.
    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.
    AdditionalSecretReferences:
      type: object
      propertyNames:
        pattern: ^[A-Za-z_][A-Za-z0-9_]*$
      additionalProperties:
        $ref: '#/components/schemas/SecretReference'
    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.
    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
    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.
    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.
    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'
    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
    SecretName:
      type: string
      minLength: 1
      maxLength: 128
      pattern: ^[A-Z][A-Z0-9_]{0,127}$
    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: {}
    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]*$
    VolumeId:
      type: string
      pattern: ^vol_[0-9a-f]{32}$
      description: >-
        BoxRouter-managed Volume ID. Provider-native volume requirements are not
        accepted.
    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.
    SecretReference:
      type: object
      additionalProperties: false
      required:
        - secret_name
      properties:
        secret_name:
          $ref: '#/components/schemas/SecretName'
    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}$
    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])
  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
  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'
  securitySchemes:
    BoxRouterKey:
      type: http
      scheme: bearer
      description: BoxRouter API key; never an upstream provider key.
    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.

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.