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

# Prepare an Image on Selected Providers

> Start background preparation using this workspace's payer-account rules, on the named
`providers` or on the `top` providers a create with the `match` body would pick now.
A named provider that cannot run the image reports its fit reason immediately; a
`match` body that no route can run is refused as that create would be. With `match`,
the response lists the picked providers in rank order, and each reason says how it
was ranked. A ready result means a usable copy existed when preparation completed;
it does not keep the copy awake afterward. Read progress from GET /images/preparations.
OCI and Dockerfile inputs use the same declared preparation capabilities as a create.
A Dockerfile response returns a content selector for status and unprepare. If no selected
offering declares prepared Dockerfile/context support, it is refused before reading the
context or starting provider work. Existing provider artifacts are already prepared.
The request JSON is limited to 64 KiB; uploaded contexts retain the artifacts API limits.




## OpenAPI

````yaml openapi.json POST /images/prepare
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:
  /images/prepare:
    post:
      tags:
        - images
      summary: Prepare an image on selected providers
      description: >
        Start background preparation using this workspace's payer-account rules,
        on the named

        `providers` or on the `top` providers a create with the `match` body
        would pick now.

        A named provider that cannot run the image reports its fit reason
        immediately; a

        `match` body that no route can run is refused as that create would be.
        With `match`,

        the response lists the picked providers in rank order, and each reason
        says how it

        was ranked. A ready result means a usable copy existed when preparation
        completed;

        it does not keep the copy awake afterward. Read progress from GET
        /images/preparations.

        OCI and Dockerfile inputs use the same declared preparation capabilities
        as a create.

        A Dockerfile response returns a content selector for status and
        unprepare. If no selected

        offering declares prepared Dockerfile/context support, it is refused
        before reading the

        context or starting provider work. Existing provider artifacts are
        already prepared.

        The request JSON is limited to 64 KiB; uploaded contexts retain the
        artifacts API limits.
      operationId: prepareImage
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PrepareImage'
            example:
              match:
                image: python:3.13-slim
              top: 2
      responses:
        '202':
          headers:
            Server-Timing:
              $ref: '#/components/headers/ServerTiming'
          description: Preparation started or already in progress.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ImagePreparationStatus'
        '400':
          $ref: '#/components/responses/Error'
        '409':
          $ref: '#/components/responses/Error'
        '422':
          $ref: '#/components/responses/Error'
        default:
          $ref: '#/components/responses/Error'
components:
  parameters:
    IdempotencyKey:
      name: Idempotency-Key
      in: header
      description: >-
        Optional for this operation. Preserve the same stable identifier when
        retrying an uncertain response; never reuse it for new work. If omitted
        where allowed, Cloud generates a request-scoped key that cannot
        reconcile a later retry. Do not include credentials or payloads.
      schema:
        $ref: '#/components/schemas/IdempotencyKeyValue'
  schemas:
    PrepareImage:
      type: object
      additionalProperties: false
      description: >
        Two forms; send exactly one. `{image, providers, sizes?}` prepares on
        the named

        providers. `{match, top?}` takes the body you send to POST /sandboxes or

        POST /sandboxes/match: Cloud ranks routes exactly as that create would
        now, under this

        workspace's payer rules, and prepares `match.image` on the first `top`
        distinct

        providers, at `match.resources`. Fields that do not choose a route, such
        as env and

        labels, are ignored. Ranking can change later, for example when prices
        or measured

        start times change, so a later create may pick a provider that was not
        prepared.
      properties:
        image:
          description: >
            Image to prepare. Only with `providers`. A string is an OCI image
            reference. Use

            {private_oci} for an image whose registry needs a login, together
            with

            `registry_credentials`; it is prepared only on routes that use your
            own provider keys.

            A Dockerfile object uses the same source text and optional uploaded
            context artifact

            as a sandbox create. Only offerings declaring that prepared source
            and context mode

            are selected. The whole JSON request is limited to 64 KiB.
          oneOf:
            - type: string
              minLength: 1
              not:
                const: provider_default
            - type: object
              additionalProperties: false
              required:
                - private_oci
              properties:
                private_oci:
                  type: string
                  minLength: 1
                  description: OCI image reference in a registry that needs a login.
            - type: object
              additionalProperties: false
              required:
                - dockerfile
              properties:
                dockerfile:
                  type: string
                  minLength: 1
                  pattern: ^[^\u0000]*$
                  description: >-
                    Dockerfile source text. No inline context files or context
                    URLs.
                context_artifact_id:
                  type: string
                  minLength: 1
                  description: >
                    Optional unexpired artifact owned by this workspace. Its tar
                    context is

                    leased through background preparation. Existing upload
                    limits apply:

                    10 MiB archive, 64 MiB expanded regular files and 2,048
                    entries.
        registry_credentials:
          type: object
          additionalProperties: false
          required:
            - username
            - password
          properties:
            username:
              type: string
              minLength: 1
              pattern: ^[^\u0000]*$
            password:
              type: string
              minLength: 1
              writeOnly: true
              pattern: ^[^\u0000]*$
          description: >
            Request-only login for a private_oci `image`; required with it and
            refused without it.

            With `match`, send it inside the match body instead. Both strings
            are non-empty, at

            most 64 KiB UTF-8 bytes together. It is passed to the provider to
            set up the copy and

            is never stored, logged, traced or returned.
        providers:
          $ref: '#/components/schemas/ImageProviders'
        sizes:
          type: array
          minItems: 1
          uniqueItems: true
          items:
            type: integer
            minimum: 1
          description: >-
            Optional memory sizes to prepare, expressed in GiB. Only with
            `providers`.
        match:
          allOf:
            - $ref: '#/components/schemas/MatchSandbox'
          description: >
            A sandbox create body. Its `image` is an OCI reference or Dockerfile
            build object,

            optionally with context_artifact_id. A missing image,
            provider_default, an existing

            provider artifact has nothing to prepare and is refused. Only

            declared prepared source/context variants are eligible. A
            private_oci image is

            prepared with the body's registry_credentials, only on routes that
            use your own

            provider keys.
        top:
          type: integer
          minimum: 1
          default: 1
          description: >
            With `match`, how many of the best-ranked distinct providers to
            prepare on. Fewer

            are prepared when fewer providers can run the create.
      allOf:
        - oneOf:
            - required:
                - providers
            - required:
                - match
        - if:
            required:
              - providers
          then:
            required:
              - image
        - if:
            required:
              - image
          then:
            required:
              - providers
        - if:
            required:
              - sizes
          then:
            required:
              - providers
        - if:
            required:
              - registry_credentials
          then:
            required:
              - providers
        - if:
            required:
              - top
          then:
            required:
              - match
    ImagePreparationStatus:
      type: object
      additionalProperties: false
      required:
        - image
        - providers
      properties:
        image:
          type: string
          description: >-
            Resolved OCI reference or the canonical sha256 content selector of a
            Dockerfile source and context.
        providers:
          type: array
          items:
            $ref: '#/components/schemas/ImageProviderState'
    IdempotencyKeyValue:
      type: string
      minLength: 1
      maxLength: 128
      description: Stable identifier for one logical mutation.
    ImageProviders:
      description: Provider names or all providers configured for this workspace.
      oneOf:
        - type: array
          minItems: 1
          uniqueItems: true
          items:
            type: string
            minLength: 1
        - type: string
          const: all
    MatchSandbox:
      allOf:
        - $ref: '#/components/schemas/CreateSandbox'
        - not:
            type: object
            properties:
              secret_values: {}
            required:
              - secret_values
      description: >
        The creation request shape without request-only secret values.
        registry_credentials

        is accepted, as on create, so a private_oci body can be sent unchanged
        to match.
    ImageProviderState:
      type: object
      additionalProperties: false
      required:
        - provider
        - size
        - state
        - reason
      properties:
        provider:
          type: string
          minLength: 1
          description: >-
            Installed provider selected through this workspace's authorized
            offerings.
        state:
          type: string
          enum:
            - queued
            - preparing
            - ready
            - inactive
            - deleting
            - failed
            - unsupported
            - absent
          x-boxrouter-open-enum: true
        size:
          type:
            - integer
            - 'null'
          minimum: 1
          description: >-
            Requested memory size in GiB, or null when the copy is not
            size-specific.
        reason:
          type:
            - string
            - 'null'
          description: Eligibility or preparation outcome for this provider.
    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.
    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
    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}$
    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'
    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}$
    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'
    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:
    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
    RequestId:
      description: >-
        Server-generated correlation identifier; contains no credentials or
        payloads.
      schema:
        type: string
  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.