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

# Create a sandbox

> Provisions a sandbox synchronously. The request blocks until the sandbox is
`ready` (~2–3s typical). Returns **201** with `status: "ready"`. Use a client
timeout of at least 90 seconds. On provisioning failure the API returns **503**.

`GET /sandboxes/{id}/wait` remains for reconnect edge cases (e.g. after resume);
it is not required after create.

## Network egress

Control outbound network access at create time with the `egress` object, or
the legacy `blockOutbound: true` shorthand (maps to `deny_all`). You cannot
set both on the same request.

| `egress.mode` | Behavior |
|---|---|
| `open` | Full outbound internet (default when omitted) |
| `deny_all` | All outbound connections blocked |
| `restricted` | Default deny + allowlist in `egress.allow` |

Change policy later with `PUT /sandboxes/{id}/egress`.




## OpenAPI

````yaml /api-reference/sandboxes.openapi.yaml post /sandboxes
openapi: 3.0.3
info:
  title: Brimble Sandbox API
  description: >
    REST API for managing Brimble sandboxes, ephemeral compute environments with

    optional persistent storage. Covers lifecycle (create / pause / resume /
    destroy),

    egress policy, runtime operations (exec, runCode, file upload/download),

    observability (logs, stats), and snapshots.


    ## Conventions


    - **Response envelope:** non-`204` JSON responses use `{ "message": string,
    "data"?: <payload> }`.
      The schemas below describe the **`data`** payload only.
    - **Errors:** every non-2xx response uses `{ "message": string }`. The
    message is
      user-facing.
    - **Async transitions:** pause / resume / destroy return immediately;
      the status change is visible on the next `GET` or in the dashboard.
      **Create is synchronous** — `POST /sandboxes` blocks until `status` is `ready`.
    - **IDs:** every `id` is a 24-char hex string.
  version: 1.0.0
  contact:
    name: Brimble Engineering
    url: https://brimble.io
servers:
  - url: https://sandbox.brimble.io
    description: Production
security:
  - brimbleKey: []
tags:
  - name: Sandboxes
    description: Lifecycle and metadata
  - name: Runtime
    description: Exec, code, files
  - name: Observability
    description: Logs and stats
  - name: Snapshots
    description: Manual & automatic snapshots
  - name: Volumes
    description: >-
      Persistent disks, pre-provisioned independently of sandbox lifecycle, then
      attached to a sandbox or project
paths:
  /sandboxes:
    post:
      tags:
        - Sandboxes
      summary: Create a sandbox
      description: >
        Provisions a sandbox synchronously. The request blocks until the sandbox
        is

        `ready` (~2–3s typical). Returns **201** with `status: "ready"`. Use a
        client

        timeout of at least 90 seconds. On provisioning failure the API returns
        **503**.


        `GET /sandboxes/{id}/wait` remains for reconnect edge cases (e.g. after
        resume);

        it is not required after create.


        ## Network egress


        Control outbound network access at create time with the `egress` object,
        or

        the legacy `blockOutbound: true` shorthand (maps to `deny_all`). You
        cannot

        set both on the same request.


        | `egress.mode` | Behavior |

        |---|---|

        | `open` | Full outbound internet (default when omitted) |

        | `deny_all` | All outbound connections blocked |

        | `restricted` | Default deny + allowlist in `egress.allow` |


        Change policy later with `PUT /sandboxes/{id}/egress`.
      operationId: createSandbox
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateSandboxInput'
            examples:
              openDefault:
                summary: Default (open egress)
                value:
                  template: node-22
                  region: auto
              denyAll:
                summary: Block all outbound traffic
                value:
                  template: python-3.12
                  region: auto
                  egress:
                    mode: deny_all
              restricted:
                summary: Allowlist specific hosts
                value:
                  template: node-22
                  region: auto
                  egress:
                    mode: restricted
                    allow:
                      - 1.1.1.1
                      - api.example.com
              legacyBlockOutbound:
                summary: Legacy shorthand (prefer egress)
                value:
                  template: python-3.12
                  blockOutbound: true
      responses:
        '201':
          description: Sandbox provisioned and ready
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CreateSandboxEnvelope'
        '400':
          $ref: '#/components/responses/BadRequest'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '503':
          description: Provisioning failed (cluster full, placement failure, etc.)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
components:
  schemas:
    CreateSandboxInput:
      type: object
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 64
          description: Display name. Auto-generated (random animal) if omitted.
        template:
          type: string
          description: >-
            Template name from the available sandbox images (e.g. `node-22`,
            `python-3.12`). Defaults to the server's configured default.
        teamId:
          type: string
          pattern: ^[a-f0-9]{24}$
          description: >-
            Create the sandbox under a team you're a member of. Copy the team ID
            from the team's settings page in the dashboard. Omit for a personal
            sandbox.
        environmentId:
          type: string
          pattern: ^[a-f0-9]{24}$
          description: Project-environment ObjectId to scope the sandbox to.
        region:
          type: string
          description: |
            Region `id` from `GET /v1/regions`, or `"auto"` to let the server
            pick one for you. Optional; defaults to `"auto"` when omitted.
        specs:
          $ref: '#/components/schemas/SandboxSpecs'
        autoDestroy:
          type: boolean
          description: If true, sandbox auto-destroys after `destroyTimeout`.
        destroyTimeout:
          type: string
          enum:
            - 30m
            - 1h
            - 3h
            - 6h
            - 12h
            - 18h
          description: Required when `autoDestroy=true`. Ignored otherwise.
        oneShot:
          type: boolean
          description: If true, sandbox auto-destroys when its main process exits.
        blockOutbound:
          type: boolean
          description: >
            Legacy shorthand for `egress.mode = deny_all`. Prefer `egress` for
            new

            integrations. Cannot be combined with `egress` on the same request.
        egress:
          $ref: '#/components/schemas/SandboxEgressConfig'
        persistent:
          type: boolean
          description: >-
            Provision a fresh per-sandbox persistent volume for the sandbox's
            workspace directory.
        persistentDiskGB:
          type: integer
          minimum: 10
          maximum: 50
          description: Required when `persistent=true`. Mutually exclusive with `volumeId`.
        volumeId:
          type: string
          pattern: ^[a-f0-9]{24}$
          description: >-
            Attach an existing detached volume. Mutually exclusive with
            `persistent` / `persistentDiskGB`.
        fromSnapshot:
          type: string
          pattern: ^[a-f0-9]{24}$
          description: Restore from a snapshot you own; replaces the template image.
        snapshotMode:
          type: string
          enum:
            - manual
            - automatic
          default: manual
        snapshotFrequency:
          type: string
          description: |
            5-field cron expression (e.g. `0 */2 * * *`).
            Required when `snapshotMode=automatic`, forbidden otherwise.
    CreateSandboxEnvelope:
      type: object
      required:
        - message
        - data
      properties:
        message:
          type: string
          example: Sandbox created
        data:
          $ref: '#/components/schemas/CreateSandboxResult'
    ErrorResponse:
      type: object
      required:
        - message
      properties:
        message:
          type: string
          description: Human-readable, user-facing error reason.
    SandboxSpecs:
      type: object
      properties:
        cpu:
          type: integer
          minimum: 1
          maximum: 2000
          description: CPU shares in Nomad MHz units.
        memory:
          type: integer
          minimum: 1
          maximum: 2048
          description: Memory in MB.
        disk:
          type: integer
          minimum: 1
          maximum: 5
          description: Ephemeral scratch disk in GB. Separate from persistent storage.
    SandboxEgressConfig:
      type: object
      required:
        - mode
      description: Outbound network policy applied to the sandbox container.
      properties:
        mode:
          $ref: '#/components/schemas/SandboxEgressMode'
        allow:
          type: array
          maxItems: 50
          items:
            type: string
            minLength: 1
            maxLength: 253
          description: >
            Allowlist for `restricted` mode. Each entry is an IPv4 address, CIDR
            range

            (e.g. `10.0.0.0/8`), or hostname (e.g. `api.example.com`). Required
            with at

            least one entry when updating to `restricted`; optional at create
            time.
          example:
            - 1.1.1.1
            - api.example.com
      example:
        mode: restricted
        allow:
          - 1.1.1.1
          - api.example.com
    CreateSandboxResult:
      type: object
      required:
        - id
        - name
        - template
        - status
        - created_at
        - expires_at
      properties:
        id:
          type: string
        name:
          type: string
        template:
          type: string
        status:
          $ref: '#/components/schemas/SandboxStatus'
        created_at:
          type: string
          format: date-time
        expires_at:
          type: string
          format: date-time
    SandboxEgressMode:
      type: string
      enum:
        - open
        - restricted
        - deny_all
      description: >
        Outbound network policy for a sandbox.


        - `open` — full outbound internet access (default).

        - `deny_all` — all outbound connections blocked; inbound Brimble API
        calls still work.

        - `restricted` — default deny with an allowlist (`allow` required on
        update).
    SandboxStatus:
      type: string
      enum:
        - starting
        - ready
        - pausing
        - paused
        - resuming
        - failed
        - destroyed
  responses:
    BadRequest:
      description: Validation error / invalid state transition
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          examples:
            invalidId:
              summary: Invalid sandbox id
              value:
                message: Invalid sandbox id
            statusTransition:
              summary: Wrong status
              value:
                message: Sandbox is paused; only ready sandboxes can be paused
            fileNotDir:
              summary: Parent dir missing on upload
              value:
                message: 'Destination directory does not exist: /work'
            duplicateVolumeName:
              summary: Duplicate volume name
              value:
                message: A volume named "node-cache" already exists
            volumeAttached:
              summary: Delete attempted on attached volume
              value:
                message: Volume is attached; detach it before deleting
    Forbidden:
      description: Plan / spending-limit / permission error
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          examples:
            planLimit:
              value:
                message: >-
                  Your plan allows up to 3 sandboxes. Destroy an existing one or
                  upgrade.
            spendingLimit:
              value:
                message: >-
                  Sandbox creation paused, spending limit reached. Raise the
                  limit or wait for the next billing cycle.
            writePerm:
              value:
                message: Permission denied writing to /etc/passwd
            volumeSizeCap:
              summary: Volume size exceeds plan cap
              value:
                message: Your plan allows volumes up to 20GB. Reduce size or upgrade.
            volumeCountCap:
              summary: Volume count exceeds plan cap
              value:
                message: >-
                  Your plan allows up to 2 volumes. Delete an existing one or
                  upgrade.
    NotFound:
      description: >-
        Sandbox or related resource not found (also returned when owned by
        another user)
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          examples:
            sandboxNotFound:
              value:
                message: Sandbox not found
            snapshotNotFound:
              value:
                message: Snapshot not found
            volumeNotFound:
              value:
                message: Volume not found
  securitySchemes:
    brimbleKey:
      type: apiKey
      in: header
      name: x-brimble-key
      description: |
        Your account-level Brimble API key. Find it in the dashboard under
        your profile drawer → **API key** (click the avatar in the sidebar).
        Available on paid plans only.

````