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

# Create a Volume File Upload

> Creates a multipart upload for a file that will appear at
`/workspace/<path>` in sandboxes. Upload every returned part URL with an
HTTP PUT containing exactly that part's bytes; save the response `ETag`
header for the completion request. Part URLs expire when the session does: six hours
after creation plus about one hour for each 70 GiB of the declared size, at most 72 hours;
`expires_at` says when.
An open upload blocks `write` sandboxes, and a live or pending `write`
attachment blocks uploads (`volume_busy`). `read` and `shared` attachments
may coexist, but their sandboxes may need to refresh their volume cache to
see the new file.




## OpenAPI

````yaml openapi.json POST /volumes/{volume_id}/uploads
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:
  /volumes/{volume_id}/uploads:
    parameters:
      - $ref: '#/components/parameters/VolumeId'
    post:
      tags:
        - volumes
      summary: Create a volume file upload
      description: >
        Creates a multipart upload for a file that will appear at

        `/workspace/<path>` in sandboxes. Upload every returned part URL with an

        HTTP PUT containing exactly that part's bytes; save the response `ETag`

        header for the completion request. Part URLs expire when the session
        does: six hours

        after creation plus about one hour for each 70 GiB of the declared size,
        at most 72 hours;

        `expires_at` says when.

        An open upload blocks `write` sandboxes, and a live or pending `write`

        attachment blocks uploads (`volume_busy`). `read` and `shared`
        attachments

        may coexist, but their sandboxes may need to refresh their volume cache
        to

        see the new file.
      operationId: createVolumeUpload
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateVolumeUpload'
            example:
              path: data/input.csv
              size: 1048576
      responses:
        '201':
          description: Created multipart upload
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/VolumeUpload'
        '400':
          $ref: '#/components/responses/Error'
        '404':
          $ref: '#/components/responses/Error'
        '409':
          $ref: '#/components/responses/Error'
        '413':
          $ref: '#/components/responses/Error'
        default:
          $ref: '#/components/responses/Error'
components:
  parameters:
    VolumeId:
      name: volume_id
      in: path
      required: true
      schema:
        $ref: '#/components/schemas/VolumeId'
    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:
    CreateVolumeUpload:
      type: object
      additionalProperties: false
      required:
        - path
        - size
      properties:
        path:
          $ref: '#/components/schemas/VolumeFilePath'
        size:
          type: integer
          minimum: 0
          maximum: 5497558138880
          description: >-
            File size in bytes, from zero through 5 TiB. Even an empty file uses
            one multipart part.
    VolumeUpload:
      type: object
      additionalProperties: false
      required:
        - id
        - volume_id
        - path
        - size
        - part_size
        - parts
        - expires_at
      description: >
        Multipart upload session. Upload each part URL with an HTTP PUT
        containing exactly

        the part bytes, then pass each response ETag header to complete. Part
        URLs and this

        session expire together, six hours after creation plus about one hour
        for each 70 GiB of the

        declared size, at most 72 hours. The file appears at `/workspace/<path>`
        in

        sandboxes after completion. An open upload blocks write sandboxes; write
        attachments

        block creating uploads. Read and shared sandboxes may need to refresh
        their volume

        cache to see the new file.
      properties:
        id:
          $ref: '#/components/schemas/UploadId'
        volume_id:
          $ref: '#/components/schemas/VolumeId'
        path:
          $ref: '#/components/schemas/VolumeFilePath'
        size:
          type: integer
          minimum: 0
          maximum: 5497558138880
        part_size:
          type: integer
          minimum: 1
        parts:
          type: array
          minItems: 1
          maxItems: 10000
          items:
            $ref: '#/components/schemas/VolumeUploadPart'
        expires_at:
          type: string
          format: date-time
    VolumeId:
      type: string
      pattern: ^vol_[0-9a-f]{32}$
      description: >-
        BoxRouter-managed Volume ID. Provider-native volume requirements are not
        accepted.
    IdempotencyKeyValue:
      type: string
      minLength: 1
      maxLength: 128
      description: Stable identifier for one logical mutation.
    VolumeFilePath:
      type: string
      minLength: 1
      maxLength: 1024
      pattern: ^(?!/)(?!.*//)(?!\.{1,2}(?:/|$))(?!.*\/\.{1,2}(?:/|$))(?!.*\u0000).+$
      description: >
        Relative path from the volume root, visible in sandboxes at
        `/workspace/<path>`.

        Empty paths, a leading slash, empty, `.` or `..` segments, NUL, and
        paths longer

        than 1024 UTF-8 bytes are refused with `invalid_request` (400).
    UploadId:
      type: string
      pattern: ^vup_[0-9a-f]{32}$
    VolumeUploadPart:
      type: object
      additionalProperties: false
      required:
        - number
        - size
        - url
      properties:
        number:
          type: integer
          minimum: 1
          maximum: 10000
        size:
          type: integer
          minimum: 0
        url:
          type: string
          format: uri
          description: >-
            Temporary URL to PUT exactly this part's bytes. The response ETag
            header is required to complete the upload.
    ApiErrorResponse:
      type: object
      required:
        - error
      description: >-
        The one error body every failing call answers. It leads with what to do:
        `retry` says whether to send the call again, `fix` says what to change,
        `outcome` says what happened, and `doc_url` links to the page that
        explains the code. Clients retry `retry: safe` for you with the same
        idempotency key.
      x-sandbox-provider-spec-codes:
        - no_eligible_route
        - knob_not_named
        - unrecognised_word
        - invalid_request
        - not_named
        - gate_unavailable
        - account_unhealthy
        - lifetime_limit
        - lifetime_used_up
        - not_running
        - invalid_state
        - sandbox_ended
        - not_found
        - provider_error
        - timeout
        - contract_violation
      properties:
        error:
          type: object
          required:
            - code
            - message
            - request_id
            - outcome
            - retry
            - retry_after_seconds
          properties:
            code:
              type: string
              description: >
                Stable machine category. Operation failures use
                SandboxProviderSpec error codes,

                including invalid_request, invalid_state, not_found,
                unauthorized,

                incompatible_requirements, no_eligible_route,
                capacity_unavailable,

                capability_unavailable, contract_violation, resource_limit,
                timeout and

                provider_error. Hosted service failures add method_not_allowed,

                unauthenticated, forbidden, actions_sandbox_protected,

                insufficient_credit, spend_limit_exceeded,

                provider_not_enabled, credentials_rejected, conflict,
                rate_limited, provider_quota, request_timeout, overloaded,

                database_unavailable, payment_method_required, payment_declined,
                promotion_already_redeemed, account_exists, and internal_error.
                unsupported_feature and

                output_limit are earlier names still returned by some routes.

                Organization and membership operations also use
                organization_changed, last_admin,

                stale_membership, directory_managed and change_in_progress. Key
                ownership and

                invitation operations use idempotency_key_reused and conflict.

                Credit refunds and purchase quotes also use
                refund_window_closed,

                nothing_to_refund, refund_below_minimum, refund_in_progress,

                purchase_in_progress, dispute_open, already_refunded,

                refund_amount_changed, refund_fee_unavailable, quote_changed,

                session_required, refund_daily_limit, refund_rate_limit,

                refund_retry_requires_support, idempotency_key_reused and
                fraud_review.

                Card setup adds card_setup_rate_limit (429, with Retry-After): a
                workspace may start five

                card setups an hour.

                A provider that refuses the request for its own plan or quota
                limit answers

                429 provider_quota with the provider's words in native_message;
                rate_limited is

                BoxRouter's own limit.

                Routing Prevent Overrides refuses a request containing
                preferences.order,

                preferences.sort or preferences.allow_fallbacks with
                routing_overrides_forbidden,

                outcome not_started and details.fields naming the prohibited
                inputs. No provider

                effect occurs.

                Launch operations also use invalid_request, secret_missing,
                agent_not_found,

                no_eligible_route and launch_failed.

                A process or terminal is observed by one API task. When that
                task stops, process

                output, result and stdin answer 410 process_observer_lost
                (reading or cancelling the

                process still works), and a terminal on a provider that cannot
                reattach it answers

                410 terminal_observer_lost. invalid_observer_hop (403) refuses
                an internal hop

                between API tasks that is not signed; clients never send one.

                Opening a preview that the provider answered with other access
                than the request

                asked for returns 502 preview_access_refused: that preview is
                never returned, and

                details say whether it was ended.

                Starting a process with wait_for_port answers 409
                port_not_listening, outcome partial,

                when the process exits before the port accepts connections or
                the port stays closed for

                30 seconds; the process is ended and details carry the output so
                far.

                A request that must store content (a volume, a saved artifact)
                under transient content

                retention answers 409 privacy_persistence_conflict with outcome
                not_started.

                A sandbox request that stops before anything is created also
                answers

                lifetime_cap_exceeded, knob_not_named and unrecognised_word
                (400), and price_unknown and

                policy_denied (409), each with outcome not_started; the Actions
                codes job_claim_lost,

                actions_authority_changed, actions_candidate_ceiling_withdrawn
                and

                actions_candidate_outside_ceiling (409) say the job's claim,
                settings or admitted routes

                changed; provider_budget_exhausted (409) says a run's provider
                budget allows no

                further create. A create that names an image Cloud cannot use
                answers 400

                image_architecture_unsupported, image_digest_resolution_failed,

                image_preparation_unsupported or
                private_image_managed_route_unavailable, and

                privacy_admission_unavailable when privacy cannot be enforced;
                each says what to change.

                Launch and source-repository refusals name the repository or
                manifest fault (code_ref_not_found,

                code_repository_*, source_*, invalid_source_manifest,
                invalid_manifest_encoding,

                invalid_run_spec, repository_*, reserved_env_refused,
                launch_run_position_conflict,

                code_reference_moved, dockerfile_context_unsupported,
                image_tag_unresolved,

                recovery_sandbox_mismatch); the GitHub-side ones
                (source_rate_limited,

                source_resolution_failed, source_resolution_timeout,
                code_repository_resolution_failed)

                are safe to resend. image_setup_timeout,
                managed_funding_unavailable,

                lifetime_funding_conflict, candidate_admission_unavailable,
                secrets_unavailable and the

                Actions candidate codes (actions_candidate_record_invalid,
                actions_create_budget_exhausted,

                actions_create_format_mismatch) complete the creation and
                extension refusals.

                dispatch_limit_reached, inline_secret_values_refused and
                launch_incomplete come from

                launch runs, provider_policy_unknown from an operation whose
                privacy evidence cannot be

                verified, and quota_exceeded from a full managed provider
                account.

                Volume operations also use volume_busy, volume_lease_lost,
                volume_not_attached,

                volume_incompatible, volume_save_failed and
                volume_restore_failed. Creating a

                sandbox on a volume whose writer is ending waits for the release
                (at most 120

                seconds) before it can answer volume_busy.

                A team member's new personal API key is paused until WorkOS
                membership sync catches

                up: it answers 403 forbidden whose message and fix say the key
                becomes active once

                the team membership syncs, usually within a minute, and the call
                can be retried.

                Clients must preserve and handle unknown categories explicitly.
              x-boxrouter-error-codes:
                account_exists:
                  - 409
                account_unhealthy:
                  - 402
                actions_authority_changed:
                  - 409
                actions_candidate_ceiling_withdrawn:
                  - 409
                actions_candidate_outside_ceiling:
                  - 409
                actions_candidate_record_invalid:
                  - 409
                actions_create_budget_exhausted:
                  - 409
                actions_create_format_mismatch:
                  - 409
                actions_sandbox_protected:
                  - 403
                agent_not_found:
                  - 404
                already_refunded:
                  - 409
                authorization_error:
                  - 403
                candidate_admission_unavailable:
                  - 503
                capability_unavailable:
                  - 409
                  - 503
                capability_unverified:
                  - 409
                card_setup_rate_limit:
                  - 429
                change_in_progress:
                  - 409
                code_ref_not_found:
                  - 404
                code_reference_moved:
                  - 409
                code_repository_not_authorized:
                  - 403
                code_repository_not_found:
                  - 404
                code_repository_not_private:
                  - 400
                code_repository_not_public:
                  - 404
                code_repository_resolution_failed:
                  - 502
                command_failed:
                  - 409
                command_observation_busy:
                  - 409
                conflict:
                  - 409
                contract_violation:
                  - 409
                credentials_rejected:
                  - 409
                  - 422
                database_unavailable:
                  - 503
                directory_managed:
                  - 409
                dispatch_limit_reached:
                  - 409
                dispute_open:
                  - 409
                dockerfile_context_unsupported:
                  - 400
                expired:
                  - 410
                forbidden:
                  - 403
                fraud_review:
                  - 409
                gate_unavailable:
                  - 409
                idempotency_key_reused:
                  - 409
                  - 422
                identity_unavailable:
                  - 503
                image_architecture_unsupported:
                  - 400
                image_digest_resolution_failed:
                  - 400
                image_preparation_unsupported:
                  - 400
                image_setup_timeout:
                  - 504
                image_tag_unresolved:
                  - 400
                inline_secret_values_refused:
                  - 422
                insufficient_credit:
                  - 402
                internal_error:
                  - 500
                invalid_manifest_encoding:
                  - 400
                invalid_observer_hop:
                  - 403
                invalid_request:
                  - 400
                  - 413
                  - 422
                  - 428
                invalid_run_spec:
                  - 400
                invalid_source_manifest:
                  - 400
                invalid_state:
                  - 409
                job_claim_lost:
                  - 409
                knob_not_named:
                  - 400
                last_admin:
                  - 409
                launch_failed:
                  - 409
                launch_incomplete:
                  - 409
                launch_run_position_conflict:
                  - 409
                lifetime_cap_exceeded:
                  - 400
                lifetime_funding_conflict:
                  - 409
                lifetime_limit:
                  - 409
                lifetime_used_up:
                  - 409
                managed_funding_unavailable:
                  - 503
                method_not_allowed:
                  - 405
                no_eligible_route:
                  - 409
                not_found:
                  - 404
                not_named:
                  - 409
                not_running:
                  - 409
                nothing_to_refund:
                  - 409
                organization_changed:
                  - 409
                output_limit:
                  - 413
                overloaded:
                  - 503
                payment_declined:
                  - 402
                payment_method_required:
                  - 409
                payment_provider_rejected:
                  - 502
                payment_provider_unavailable:
                  - 503
                payments_unavailable:
                  - 503
                policy_denied:
                  - 409
                port_not_listening:
                  - 409
                preview_access_refused:
                  - 502
                price_unknown:
                  - 409
                privacy_admission_unavailable:
                  - 400
                privacy_persistence_conflict:
                  - 409
                private_image_managed_route_unavailable:
                  - 400
                process_observer_lost:
                  - 410
                promotion_already_redeemed:
                  - 409
                promotion_code_invalid:
                  - 404
                provider_budget_exhausted:
                  - 409
                provider_error:
                  - 502
                provider_not_enabled:
                  - 409
                provider_policy_unknown:
                  - 409
                provider_quota:
                  - 429
                purchase_in_progress:
                  - 409
                quota_exceeded:
                  - 429
                quote_changed:
                  - 409
                rate_limited:
                  - 429
                recovery_sandbox_mismatch:
                  - 409
                refund_amount_changed:
                  - 409
                refund_below_minimum:
                  - 409
                refund_daily_limit:
                  - 429
                refund_fee_unavailable:
                  - 409
                refund_in_progress:
                  - 409
                refund_rate_limit:
                  - 429
                refund_retry_requires_support:
                  - 409
                refund_window_closed:
                  - 409
                repository_file_unsupported:
                  - 400
                repository_too_large:
                  - 400
                request_timeout:
                  - 408
                reserved_env_refused:
                  - 400
                  - 422
                resource_limit:
                  - 413
                  - 507
                routing_overrides_forbidden:
                  - 400
                run_limit_exceeded:
                  - 429
                sandbox_ended:
                  - 410
                secret_missing:
                  - 400
                secrets_unavailable:
                  - 503
                session_required:
                  - 403
                source_file_not_found:
                  - 400
                source_file_too_large:
                  - 413
                source_rate_limited:
                  - 503
                source_ref_not_found:
                  - 400
                source_repository_not_accessible:
                  - 404
                source_repository_not_found:
                  - 400
                source_repository_not_private:
                  - 400
                source_repository_not_public:
                  - 400
                source_resolution_failed:
                  - 502
                source_resolution_timeout:
                  - 504
                spend_limit_exceeded:
                  - 402
                stale_membership:
                  - 409
                terminal_closed:
                  - 410
                terminal_observer_lost:
                  - 410
                timeout:
                  - 504
                unauthenticated:
                  - 401
                unavailable:
                  - 503
                unrecognised_word:
                  - 400
                unsupported_feature:
                  - 422
                volume_busy:
                  - 409
                volume_incompatible:
                  - 409
                volume_lease_lost:
                  - 409
                volume_not_attached:
                  - 409
                volume_restore_failed:
                  - 502
                volume_save_failed:
                  - 409
                  - 502
                volume_unavailable:
                  - 409
                wrong_region:
                  - 421
              x-boxrouter-error-retry:
                account_exists: after_fix
                account_unhealthy: after_fix
                actions_authority_changed: after_fix
                actions_candidate_ceiling_withdrawn: after_fix
                actions_candidate_outside_ceiling: after_fix
                actions_candidate_record_invalid: after_fix
                actions_create_budget_exhausted: after_fix
                actions_create_format_mismatch: after_fix
                actions_sandbox_protected: never
                agent_not_found: after_fix
                already_refunded: never
                authorization_error: check_outcome
                candidate_admission_unavailable: never
                capability_unavailable: check_outcome
                capability_unverified: after_fix
                card_setup_rate_limit: safe
                change_in_progress: safe
                code_ref_not_found: after_fix
                code_reference_moved: after_fix
                code_repository_not_authorized: after_fix
                code_repository_not_found: after_fix
                code_repository_not_private: after_fix
                code_repository_not_public: after_fix
                code_repository_resolution_failed: safe
                command_failed: after_fix
                command_observation_busy: safe
                conflict: check_outcome
                contract_violation: check_outcome
                credentials_rejected: after_fix
                database_unavailable: check_outcome
                directory_managed: never
                dispatch_limit_reached: after_fix
                dispute_open: after_fix
                dockerfile_context_unsupported: after_fix
                expired: check_outcome
                forbidden: after_fix
                fraud_review: safe
                gate_unavailable: after_fix
                idempotency_key_reused: after_fix
                identity_unavailable: safe
                image_architecture_unsupported: after_fix
                image_digest_resolution_failed: after_fix
                image_preparation_unsupported: after_fix
                image_setup_timeout: after_fix
                image_tag_unresolved: after_fix
                inline_secret_values_refused: after_fix
                insufficient_credit: after_fix
                internal_error: never
                invalid_manifest_encoding: after_fix
                invalid_observer_hop: after_fix
                invalid_request: after_fix
                invalid_run_spec: after_fix
                invalid_source_manifest: after_fix
                invalid_state: check_outcome
                job_claim_lost: after_fix
                knob_not_named: after_fix
                last_admin: after_fix
                launch_failed: check_outcome
                launch_incomplete: check_outcome
                launch_run_position_conflict: after_fix
                lifetime_cap_exceeded: after_fix
                lifetime_funding_conflict: after_fix
                lifetime_limit: check_outcome
                lifetime_used_up: after_fix
                managed_funding_unavailable: safe
                method_not_allowed: after_fix
                no_eligible_route: after_fix
                not_found: check_outcome
                not_named: after_fix
                not_running: after_fix
                nothing_to_refund: never
                organization_changed: safe
                output_limit: check_outcome
                overloaded: safe
                payment_declined: after_fix
                payment_method_required: after_fix
                payment_provider_rejected: check_outcome
                payment_provider_unavailable: check_outcome
                payments_unavailable: safe
                policy_denied: after_fix
                port_not_listening: after_fix
                preview_access_refused: never
                price_unknown: after_fix
                privacy_admission_unavailable: after_fix
                privacy_persistence_conflict: after_fix
                private_image_managed_route_unavailable: after_fix
                process_observer_lost: check_outcome
                promotion_already_redeemed: never
                promotion_code_invalid: after_fix
                provider_budget_exhausted: never
                provider_error: check_outcome
                provider_not_enabled: after_fix
                provider_policy_unknown: after_fix
                provider_quota: after_fix
                purchase_in_progress: safe
                quota_exceeded: safe
                quote_changed: after_fix
                rate_limited: safe
                recovery_sandbox_mismatch: check_outcome
                refund_amount_changed: after_fix
                refund_below_minimum: never
                refund_daily_limit: safe
                refund_fee_unavailable: safe
                refund_in_progress: safe
                refund_rate_limit: safe
                refund_retry_requires_support: never
                refund_window_closed: never
                repository_file_unsupported: after_fix
                repository_too_large: after_fix
                request_timeout: safe
                reserved_env_refused: after_fix
                resource_limit: check_outcome
                routing_overrides_forbidden: after_fix
                run_limit_exceeded: after_fix
                sandbox_ended: check_outcome
                secret_missing: after_fix
                secrets_unavailable: never
                session_required: never
                source_file_not_found: after_fix
                source_file_too_large: after_fix
                source_rate_limited: safe
                source_ref_not_found: after_fix
                source_repository_not_accessible: after_fix
                source_repository_not_found: after_fix
                source_repository_not_private: after_fix
                source_repository_not_public: after_fix
                source_resolution_failed: safe
                source_resolution_timeout: safe
                spend_limit_exceeded: after_fix
                stale_membership: after_fix
                terminal_closed: after_fix
                terminal_observer_lost: after_fix
                timeout: check_outcome
                unauthenticated: after_fix
                unavailable: safe
                unrecognised_word: after_fix
                unsupported_feature: after_fix
                volume_busy: after_fix
                volume_incompatible: after_fix
                volume_lease_lost: after_fix
                volume_not_attached: after_fix
                volume_restore_failed: check_outcome
                volume_save_failed: check_outcome
                volume_unavailable: after_fix
                wrong_region: after_fix
            message:
              type: string
            details:
              type: object
              description: >
                Code-specific context, spelled for programs. no_eligible_route
                carries considered,

                excluded, closest and nearest; knob_not_named the knob and its
                values;

                unrecognised_word the path, the word and the known ones;
                not_named the gate, what

                the call needed beyond the grant and the corrected requirements
                line; gate_unavailable

                the gate and account; lifetime_limit the ceiling and this run. A
                creation that

                stopped before a sandbox existed may also carry
                `fallbacks_allowed`, `stopped_reason`

                and `attempts`, spelled and bounded as in the sandbox route
                selection.

                capability_unavailable may identify the needed capability. A
                launch preparation failure can follow sandbox creation;

                inspect outcome and cleanup details before creating another.
                Clients must

                preserve fields they do not know.
              properties:
                selection_version:
                  type: integer
                  minimum: 0
                  description: >-
                    Current selected organization version for
                    organization_changed.
                considered:
                  type: integer
                  minimum: 0
                  description: no_eligible_route only; how many routes were considered.
                excluded:
                  type: array
                  description: >-
                    no_eligible_route only; every considered route that cannot
                    serve the request. Each entry names its `provider` and the
                    checks it failed (`failed` for a sandbox request, `reasons`
                    and `exclusions` for a launch); clients must preserve fields
                    they do not know.
                  items:
                    type: object
                    required:
                      - provider
                    properties:
                      provider:
                        type: string
                closest:
                  type:
                    - object
                    - 'null'
                  description: >-
                    no_eligible_route only; the excluded route that fails fewest
                    checks, or null.
                nearest:
                  type: array
                  description: >-
                    no_eligible_route only; the smallest changes that would make
                    a route eligible, nearest first. An entry names the change
                    (`change` and `then_eligible` for a sandbox request,
                    `provider`, `distance` and `failing` for a launch); clients
                    must preserve fields they do not know.
                  items:
                    type: object
                requirement:
                  type: string
                  description: >-
                    The SandboxProviderSpec family whose requirement would make
                    this operation available.
                purchase_id:
                  type: string
                  description: >-
                    Retained failed credit purchase ID, when a saved-card charge
                    was declined.
                preview:
                  $ref: '#/components/schemas/CreditRefundPreview'
                  description: >-
                    Current refund preview returned with 409
                    refund_amount_changed.
                quote:
                  $ref: '#/components/schemas/CreditPurchaseQuote'
                  description: Current purchase quote returned with 409 quote_changed.
                redemption:
                  $ref: '#/components/schemas/CreditPromotion'
                  description: >-
                    Original promotion redemption returned with 409
                    promotion_already_redeemed.
                scope:
                  type: string
                  enum:
                    - workspace
                  description: >-
                    spend_limit_exceeded only; which configured cap refused
                    admission.
                period:
                  type: string
                  enum:
                    - total
                    - daily
                    - monthly
                  description: spend_limit_exceeded only; the cap period that applies.
                limit_nanodollars:
                  type: integer
                  minimum: 1
                  description: spend_limit_exceeded only; exact configured USD nanodollars.
                requested_nanodollars:
                  type: integer
                  minimum: 1
                  description: spend_limit_exceeded only; exact proposed managed hold.
                sign_in_method:
                  type: string
                  description: >-
                    account_exists only: how the existing account with this
                    verified address signs in (google, github, email or other).
                masked_email:
                  type: string
                  description: >-
                    account_exists only: that account's address, masked as
                    `t…@gmail.com`.
                failure_reason:
                  type: string
                  maxLength: 100
                  description: >-
                    Short safe card-decline reason; raw Stripe messages are not
                    returned.
                names:
                  type: array
                  uniqueItems: true
                  items:
                    $ref: '#/components/schemas/SecretName'
                  description: All manifest secret names that could not be resolved.
                fields:
                  type: array
                  uniqueItems: true
                  items:
                    type: string
                    enum:
                      - preferences.order
                      - preferences.sort
                      - preferences.allow_fallbacks
                  description: >-
                    routing_overrides_forbidden only; the prohibited request
                    inputs.
                volume_id:
                  type: string
                  description: BoxRouter Volume ID involved in the error.
                writer_sandbox_id:
                  type: string
                  description: >-
                    volume_busy only; the sandbox that holds the volume's writer
                    lease, when it is a recorded sandbox.
                checkpoint:
                  type: integer
                  minimum: 1
                  description: Volume checkpoint involved in the error.
                limit_bytes:
                  type: integer
                  minimum: 0
                observed_bytes:
                  type: integer
                  minimum: 0
                mismatch:
                  type: object
                  additionalProperties: false
                  required:
                    - field
                    - recorded
                    - requested
                  properties:
                    field:
                      type: string
                      enum:
                        - image
                        - arch
                    recorded:
                      type: string
                    requested:
                      type: string
                volume_phase:
                  type: string
                  enum:
                    - check
                    - branch
                    - lease
                    - identity
                    - install
                    - credential
                    - mount
                    - metadata
                    - checkpoint
                    - commit
                    - write
                phase:
                  type: string
                  enum:
                    - code
                    - setup
                    - files
                    - background
                    - previews
                  description: Preparation phase that failed.
                command:
                  type: string
                  description: Command whose nonzero exit stopped launch preparation.
                exit_code:
                  type: integer
                  description: >-
                    Exit code of the failed command, or of the process that
                    exited before its port listened.
                stderr_tail:
                  type: string
                  description: >
                    At most the final 4 KiB of stderr from the failed command,
                    with resolved secret values

                    redacted. port_not_listening carries the same tail of the
                    process's stderr.
                stdout_tail:
                  type: string
                  description: >-
                    port_not_listening only; at most the final 4 KiB of the
                    process's stdout.
                agent:
                  type: string
                  description: Unknown catalog agent name.
                port:
                  type: integer
                  minimum: 1
                  maximum: 65535
                  description: >-
                    The sandbox port whose preview was refused
                    (preview_access_refused) or that never accepted connections
                    (port_not_listening).
                revoked:
                  type: boolean
                  description: >
                    preview_access_refused only; true when the unwanted preview
                    was ended and no

                    access to the port remains. False: it may still be reachable
                    (outcome unknown).
                preview_id:
                  type: string
                  description: >
                    preview_access_refused only; the identifier the provider
                    gave the preview that may

                    still be reachable. Present only for a public preview.
                url:
                  type: string
                  format: uri
                  description: >
                    preview_access_refused only; the address of a public preview
                    that may still be

                    reachable. Never present for a private preview, whose
                    address holds a credential,

                    and never when it was ended.
            request_id:
              type: string
            doc_url:
              type:
                - string
                - 'null'
              format: uri
              description: >-
                Where the documentation explains this code:
                `https://boxrouter.ai/docs/error-codes#` followed by the code,
                for example
                `https://boxrouter.ai/docs/error-codes#no_eligible_route`. The
                page has one anchored row per code with what it means and what
                to do. BoxRouter sends it with every error; it is null for a
                code that has no documented row. Clients must accept an error
                body that omits it.
            fix:
              type:
                - string
                - 'null'
              minLength: 1
              description: >-
                Actionable change for the caller when the request cannot be
                completed. A refusal (4xx) always carries one; a server or
                provider fault, including an error event that ends a terminal or
                process stream, can answer null.
            operation_id:
              type:
                - string
                - 'null'
              description: >-
                Durable server operation ID for reconciliation after uncertain
                mutations.
            outcome:
              type: string
              enum:
                - not_started
                - completed
                - partial
                - unknown
              description: >
                What is known about the requested side effect. `not_started`
                means nothing ran

                anywhere, at BoxRouter or at a provider; a launch-setup or
                process-start failure

                after dispatch is `partial` or `unknown`. Unknown and partial
                forbid automatic

                replay and always carry `retry: check_outcome`. An HTTP status
                alone does not establish

                outcome.

                Many refusals report not_started before the provider is asked
                anything. A read

                (GET or HEAD) has no side effect to be unsure about, so its
                failures report

                not_started and carry the code's own retry value. A launch
                preparation failure

                happens after the sandbox was created and setup commands may
                already have run, and

                deleting the sandbox does not undo effects outside it: it
                reports partial once the

                cleanup is confirmed and unknown if it is not. A process that
                wait_for_port ended

                reports partial. volume_restore_failed on a resume or create,

                and volume_save_failed after the guest was read, report unknown
                or partial; they

                carry the operation_id when the call was recorded, and their fix
                does not offer to

                send the call again unchanged.
            retry:
              type: string
              enum:
                - safe
                - after_fix
                - check_outcome
                - never
              x-boxrouter-open-enum: true
              description: >
                What the caller may do next, fixed per code
                (x-boxrouter-error-retry): safe resends the

                same request, after_fix changes the request or setup first,
                check_outcome reads

                `outcome` and the resource before resending, never does not
                resend. Branch on this

                instead of the HTTP status.
            retry_after_seconds:
              type:
                - integer
                - 'null'
              minimum: 0
              description: >-
                How long to wait before a `retry: safe` resend, as the
                Retry-After header says it; null when there is no wait to name.
            provider:
              type:
                - string
                - 'null'
            native_code:
              type:
                - string
                - 'null'
              description: >-
                The provider's machine-readable error code, unchanged, when it
                reports one.
            native_status:
              type:
                - integer
                - 'null'
              description: >-
                The provider's HTTP status, unchanged, when the failure came
                from a provider response.
            native_message:
              type:
                - string
                - 'null'
              maxLength: 4096
              description: >-
                The provider's own error message when it answered with an HTTP
                4xx refusal (for example a plan limit); null otherwise. Capped
                at 512 characters, with whitespace collapsed. Credential secrets
                and token-shaped strings are removed before return. The literal
                `[redacted]` marker is used when all credential secret fields
                are at least 12 characters; legacy saved secrets shorter than 12
                characters are removed without the marker.
    CreditRefundPreview:
      type: object
      additionalProperties: false
      required:
        - unused
        - held_excluded
        - fee
        - principal
        - tax
        - total
        - card
      properties:
        unused:
          type: integer
          minimum: 0
          description: Unused purchased credit in US cents, before held credit is excluded.
        held_excluded:
          type: integer
          minimum: 0
          description: Credit held for running sandboxes, in US cents.
        fee:
          type: integer
          minimum: 0
          description: Original Stripe processing fee retained, in US cents.
        principal:
          type: integer
          minimum: 0
          description: Tax-exclusive principal returned to the original card, in US cents.
        tax:
          type: integer
          minimum: 0
          description: Tax Stripe adds to the credit note, in US cents.
        total:
          type: integer
          minimum: 0
          description: Total Stripe will refund, in US cents.
        card:
          $ref: '#/components/schemas/MaskedCreditCard'
    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}$
    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}$
  responses:
    Error:
      description: >
        Structured error. `retry` says whether to send the call again (`now`,
        `after_fix`, `inspect` or `never`) and `retry_after_seconds` how long to
        wait first. Unowned and unknown resource IDs both return 404. Admission
        overload returns 503 with code overloaded, outcome not_started and
        Retry-After. It reserves no operation and dispatches no provider work.
        Shared workspace/provider admission budgets return 429 with code
        rate_limited, outcome not_started and Retry-After. An incomplete request
        body can return 408 request_timeout before dispatch. Durable-state
        failures return 503 database_unavailable with outcome unknown; do not
        infer that execution did not occur. A read (GET or HEAD) that meets the
        same failure changed nothing: its message says the state is temporarily
        unavailable, and it is safe to repeat. A provider that cannot be reached
        when a create is prepared returns 502 provider_error with outcome
        not_started; nothing was created, and the create can be retried with the
        same Idempotency-Key or sent to another provider with `providers`.

        Native errors are sanitized; upstream credentials, request bodies and

        another workspace's metadata are never included. Server-Timing is
        present only when the response reached Dispatcher or held-response
        preparation; an earlier response, including pre-Dispatcher admission
        overload, may omit it.
      headers:
        X-Request-ID:
          $ref: '#/components/headers/RequestId'
        Server-Timing:
          $ref: '#/components/headers/ServerTiming'
        Retry-After:
          description: >-
            Seconds before retrying an eligible read; not permission to replay a
            mutation.
          schema:
            type: integer
            minimum: 0
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ApiErrorResponse'
  headers:
    RequestId:
      description: >-
        Server-generated correlation identifier; contains no credentials or
        payloads.
      schema:
        type: string
    ServerTiming:
      description: >
        Durations of server-side phases, for diagnosing slow requests. Phase
        names are not stable

        and may change.
      schema:
        type: string
      x-boxrouter-server-timing:
        version: 1
        unit: ms
        serviceAggregateMetric: service
        maxHeaderBytes: 8192
        maxOperationRows: 32
        decimalPlaces: 3
        safeNamePattern: ^[A-Za-z0-9_.:-]{1,64}$
        aggregates:
          - metric: service
            fieldStem: cloud
          - metric: database
            fieldStem: database
          - metric: code
            fieldStem: code
          - metric: provider_connect
            fieldStem: provider_connect
          - metric: provider_wait
            fieldStem: provider_wait
          - metric: accept
            fieldStem: accept
          - metric: queue
            fieldStem: queue
          - metric: admission
            fieldStem: admission
          - metric: encode
            fieldStem: encode
        aggregateGrammar:
          duration:
            parameter: dur
            type: number
            finite: true
            minimum: 0
          descriptionParameter: desc
          unknownDescription: unknown
          missingMeans: unreported
        operationGrammar:
          metric: op
          nameParameter: desc
          namePattern: safeNamePattern
          start:
            parameter: start
            type: number
            finite: true
            sign: signed
            relativeTo: recorderWindow
          duration:
            parameter: dur
            type: number
            finite: true
            minimum: 0
          outcomeParameter: outcome
          outcomes:
            - returned
            - raised
          requiresAggregateDependencyUnion: false
        omissionGrammar:
          metric: op-omitted
          count:
            parameter: desc
            type: integer
            minimum: 0
          duration:
            parameter: dur
            type: number
            finite: true
            minimum: 0
            sumOf: knownOmittedMeasuredRows
          requiresAggregateDependencyUnion: false
        coverageGrammar:
          metric: op-coverage
          descriptionParameter: desc
          unknownValue: unknown
          absentState: unreported
          states:
            - unknown
            - unreported
          absenceImpliesCompleteness: false
  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.