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

# Attach a sandbox to a CI runner

> Makes a sandbox a long-lived runner for the organization. Send `sandbox` to create a new sandbox and attach it, or `sandboxId` to attach one you already have. Send exactly one of the two.

An existing sandbox must be `ready`, created from the `github-actions-runner` template, and in the same workspace as the runner. The call registers the sandbox with GitHub and starts the runner in it, which can take up to two minutes.

While attached, the sandbox does not expire and is billed for as long as it runs. If the runner cannot start in a sandbox created by this call, that sandbox is removed.

Only runners in `reuse` mode accept sandboxes.

**API key scope:** `ci_runner.update`

**Team role permission:** `ci_runner.update`



## OpenAPI

````yaml api-reference/core.openapi.yaml POST /v1/ci-runners/{id}/sandboxes
openapi: 3.0.3
info:
  title: Brimble API
  version: 1.0.0
  description: >
    The Brimble API lets you deploy and manage projects, databases, domains,
    sandboxes, object storage and more.


    ## Base URL


    All endpoints are relative to `https://api.brimble.io/core`, for example
    `GET https://api.brimble.io/core/v1/projects`.


    ## Authentication


    Create an API key in the dashboard under **Settings → API keys** and send it
    in the `x-brimble-key` header:


    ```

    x-brimble-key: <your API key>

    ```


    Each API key carries permission scopes such as `project.read` or
    `project.deploy`. Every endpoint lists the scope it needs; a key without
    that scope gets `403`. Dashboard session tokens (`Authorization: Bearer
    <token>`) are also accepted, but integrations should use API keys.


    ## Teams


    Endpoints act on your personal workspace unless you pass a team. Most
    endpoints accept `teamId` (as a query parameter, or in the body for writes).
    API keys created for a team act in that team. Inside a team, your team role
    must also allow the action; each endpoint lists the team permission it
    checks.


    ## Responses


    Successful responses are JSON with this shape:


    ```json

    { "message": "Projects fetched", "data": { } }

    ```


    Errors return a `message`:


    ```json

    { "message": "Project not found", "data": {} }

    ```


    Requests that fail validation return `400` with every invalid field:


    ```json

    { "errors": [{ "type": "field", "msg": "Project name should be provided",
    "path": "name", "location": "body" }] }

    ```


    ## Rate limits


    Requests made with an API key are rate limited. Responses include
    `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and
    `X-RateLimit-Bucket`. Command execution and file transfer endpoints use
    separate buckets.


    ## Idempotency


    Endpoints that accept an `Idempotency-Key` header return the original result
    when retried with the same key and body, instead of repeating the operation.


    ## Streaming


    Command execution endpoints can stream output as Server-Sent Events
    (`text/event-stream`). Each event is `data: <json>` where the JSON has a
    `type` of `stdout`, `stderr`, `done` or `error`.


    ## Webhooks


    To receive events instead of polling, configure a webhook with `PATCH
    /v1/webhooks`. See the [Webhooks guide](/webhooks/overview) and the [Webhook
    events reference](/webhooks/events) for every payload.
  contact:
    name: Brimble
    url: https://brimble.io
servers:
  - url: https://api.brimble.io/core
    description: Production
security:
  - ApiKeyAuth: []
  - BearerAuth: []
tags:
  - name: Projects
    description: Create, deploy, configure and manage projects.
  - name: Deployments and Logs
    description: Deployment history, build logs and request logs.
  - name: Environments
    description: Project environments (production, staging, previews) and their variables.
  - name: Secrets
    description: >-
      Secrets (environment variables) set on a single project. Not to be
      confused with Environments, which group projects into production, staging
      and preview. Secret values are encrypted at rest and returned encrypted;
      use `POST /v1/decrypt` to read them.
  - name: Webhooks
    description: >-
      Receive events from Brimble at your own endpoint (or a Discord or Slack
      channel). Configure the destination and events with `PATCH /v1/webhooks`;
      use `*` to receive every event.


      See the [Webhooks guide](/webhooks/overview) for setup, and the [Webhook
      events reference](/webhooks/events) for the payload each event delivers.


      | Event | Sent when |

      | --- | --- |

      | `project.created` | A project is created |

      | `project.updated` | A project is updated |

      | `project.deleted` | A project is deleted |

      | `project.domain.updated` | A project domain changes |

      | `deployment.created` | A deployment is queued |

      | `deployment.started` | A deployment starts |

      | `deployment.success` | A deployment succeeds |

      | `deployment.failed` | A deployment fails |

      | `environment.variables.added` | Environment variables are added |

      | `environment.variables.updated` | Environment variables are updated |

      | `environment.variables.deleted` | Environment variables are deleted |

      | `domain.created` | A domain is added |

      | `domain.purchased` | A domain is purchased |

      | `domain.renewed` | A domain is renewed |

      | `domain.expired` | A domain expires |

      | `dns.record.created` | A DNS record is created |

      | `dns.record.updated` | A DNS record is updated |

      | `dns.record.deleted` | A DNS record is deleted |

      | `database.created` | A database is created |

      | `database.backup.completed` | A database backup completes |

      | `autoscaling.group.created` | An autoscaling rule is created |

      | `autoscaling.group.updated` | An autoscaling rule is updated |

      | `autoscaling.group.deleted` | An autoscaling rule is deleted |

      | `payment.successful` | A payment succeeds |

      | `payment.failed` | A payment fails |


      Test deliveries sent with `POST /v1/webhooks/test` include the header
      `X-Brimble-Test: true`.
  - name: Git Providers
    description: Connected git accounts, repositories and linking repositories to projects.
  - name: Domains
    description: Custom domains, DNS records and domain registration.
  - name: Databases
    description: 'Managed databases: provisioning, backups, users and linking to projects.'
  - name: Log Drains
    description: Stream logs to external destinations.
  - name: Sandboxes
    description: Isolated sandboxes for running code, with files and snapshots.
  - name: CI Runners
    description: Run GitHub Actions jobs for an organization on Brimble sandboxes.
  - name: Object Storage
    description: S3-compatible buckets, objects and credentials.
  - name: Scaling and Networking
    description: Autoscaling rules, networking and CDN cache, and rate limits.
  - name: Teams
    description: Teams, members, invitations and roles.
  - name: Tags
    description: Label projects with tags and filter by them.
  - name: Catalog
    description: Regions, frameworks and plans. No authentication required.
paths:
  /v1/ci-runners/{id}/sandboxes:
    post:
      tags:
        - CI Runners
      summary: Attach a sandbox to a CI runner
      description: >-
        Makes a sandbox a long-lived runner for the organization. Send `sandbox`
        to create a new sandbox and attach it, or `sandboxId` to attach one you
        already have. Send exactly one of the two.


        An existing sandbox must be `ready`, created from the
        `github-actions-runner` template, and in the same workspace as the
        runner. The call registers the sandbox with GitHub and starts the runner
        in it, which can take up to two minutes.


        While attached, the sandbox does not expire and is billed for as long as
        it runs. If the runner cannot start in a sandbox created by this call,
        that sandbox is removed.


        Only runners in `reuse` mode accept sandboxes.


        **API key scope:** `ci_runner.update`


        **Team role permission:** `ci_runner.update`
      operationId: attachCiRunnerSandbox
      parameters:
        - name: id
          in: path
          required: true
          description: ID of the CI runner.
          schema:
            type: string
            pattern: ^[a-fA-F0-9]{24}$
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                sandboxId:
                  type: string
                  pattern: ^[a-fA-F0-9]{24}$
                  description: ID of an existing sandbox to attach.
                sandbox:
                  type: object
                  description: A new sandbox to create and attach.
                  required:
                    - size
                    - region
                  properties:
                    name:
                      type: string
                      minLength: 1
                      maxLength: 64
                      description: >-
                        Name of the sandbox. Omit to generate one from the
                        organization name.
                    size:
                      type: string
                      description: Sandbox size, for example `sbx.small`.
                    region:
                      type: string
                      pattern: ^[a-fA-F0-9]{24}$
                      description: ID of the sandbox region.
      responses:
        '201':
          description: Sandbox attached as a CI runner
          content:
            application/json:
              schema:
                type: object
                required:
                  - message
                  - data
                properties:
                  message:
                    type: string
                    description: Human-readable result of the request.
                    example: Sandbox attached as a CI runner
                  data:
                    $ref: '#/components/schemas/CiRunnerSandbox'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: The sandbox is already attached to a CI runner
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          $ref: '#/components/responses/ServerError'
        '502':
          description: >-
            The runner could not start in the sandbox or did not register with
            GitHub
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
components:
  schemas:
    CiRunnerSandbox:
      type: object
      properties:
        id:
          type: string
          description: ID of the attachment.
        sandbox_id:
          type: string
          description: ID of the attached sandbox.
        runner_name:
          type: string
          description: Name the runner has in GitHub.
        status:
          type: string
          enum:
            - attaching
            - online
            - offline
            - detached
          description: >-
            `online` means GitHub can see the runner. `offline` means it cannot;
            the runner is restarted automatically if the sandbox is still ready.
        last_online_at:
          type: string
          format: date-time
          nullable: true
          description: When the runner was last seen online.
        error:
          type: string
          nullable: true
          description: Last error from restarting the runner, if any.
        created_at:
          type: string
          format: date-time
          description: When the sandbox was attached.
      required:
        - id
        - sandbox_id
        - runner_name
        - status
        - last_online_at
        - error
        - created_at
    ErrorResponse:
      type: object
      required:
        - message
      properties:
        message:
          type: string
          description: Human-readable error message.
          example: Project not found
        data:
          type: object
          description: Always empty on errors.
          example: {}
    ValidationErrorResponse:
      type: object
      required:
        - errors
      properties:
        errors:
          type: array
          description: One entry per invalid field.
          items:
            type: object
            properties:
              type:
                type: string
                description: Always `field`.
                example: field
              value:
                description: Value that was sent.
              msg:
                type: string
                description: What is wrong with the value.
                example: Invalid value
              path:
                type: string
                description: >-
                  Name of the invalid field. Nested fields use dots, for example
                  `repo.branch`.
                example: name
              location:
                type: string
                description: Part of the request the field was in.
                enum:
                  - body
                  - query
                  - params
                  - headers
                  - cookies
  responses:
    ValidationError:
      description: Request validation failed
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ValidationErrorResponse'
    Unauthorized:
      description: Missing, invalid or expired credentials
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    Forbidden:
      description: >-
        The API key lacks the required scope, your team role lacks the
        permission, or the feature is not available on your account
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    NotFound:
      description: Resource not found
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    ServerError:
      description: Unexpected server error
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: x-brimble-key
      description: >-
        API key created in the dashboard under Settings → API keys. Each key
        carries permission scopes; every endpoint lists the scope it needs.
    BearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: Dashboard session token. Use an API key for integrations.

````

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