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

# Execute a sandbox runtime action

> Single dispatch endpoint for runtime actions on a running sandbox. Currently `update_egress_policy` is supported: it replaces the egress allowlist. Only a sandbox created with `egress_control=true` can be updated; the allowlist is a full replacement, not a merge.

`applied=immediate` means the change is already in effect (existing connections are not promised to drop); `applied=eventual` means the change is accepted and still propagating. A sandbox without `egress_control` returns 400.

**Only a `running` sandbox accepts this call.** `paused` and other transitional states return 409: a paused sandbox has no writable live network, and a resume does not replay a change submitted while paused — accepting the call would silently do nothing. Resume the sandbox first with `POST /sandboxes/{id}/resume`.




## OpenAPI

````yaml /api-spec/sandbox_api.yaml post /sandboxes/{id}/actions
openapi: 3.0.3
info:
  title: GMI Sandbox API
  description: >-
    REST API for GMI Sandbox: create isolated execution environments from
    templates or durable snapshots, manage their lifecycle on the control plane,
    and run commands or transfer files through each sandbox's own data plane.
  contact:
    name: GMI Cloud Support
    email: support@gmicloud.ai
  version: '2.0'
servers:
  - url: https://console.gmicloud.ai/api/v2
    description: Control plane
security:
  - bearerAuth: []
tags:
  - name: Sandbox
    description: |
      Sandbox instance lifecycle management (Sandbox v2, /api/v2/sandboxes)
  - name: SandboxSnapshot
    description: Durable sandbox snapshot management (Sandbox v2, `/api/v2/snapshots`)
  - name: SandboxTemplate
    description: Sandbox template management (Sandbox v2, `/api/v2/templates`)
  - name: Sandbox-Exec
    description: >
      Sandbox execution data plane (accessed via the sandbox host and sandbox
      access token). These endpoints are not under the control plane's `/api/v2`
      path; clients connect directly to `https://{sandbox_key}.{domain}` and
      authenticate with `X-Access-Token`.
  - name: Sandbox-Shell
    description: >
      Sandbox interactive terminal (PTY) data plane. **These endpoints are not
      under `/api/v2`**: clients connect directly to
      `https://{sandbox_key}.{domain}` and authenticate with `X-Access-Token`.
      `{sandbox_key}`, `{domain}` and the `X-Access-Token` value are the
      `sandbox_key`, `domain` and `sandbox_access_token` returned by the
      control-plane create/connect endpoints; no extra endpoint is needed.


      Each sandbox has exactly **one logical shell session** and at most **one
      active WebSocket connection** at a time (a second concurrent attach is
      refused at handshake with `409`). Disconnecting does not destroy the shell
      — re-attaching resumes the same session; only `POST /shell/control` with
      `action=close` destroys it.
  - name: Sandbox-Files
    description: >
      Sandbox file data plane (`/files`). **These endpoints are not under
      `/api/v2`** and do not use `Authorization: Bearer`: they are served by the
      sandbox's own data-plane entry point — clients connect directly to
      `https://{sandbox_key}.{domain}` and authenticate with `X-Access-Token`.
      `{sandbox_key}` is the `sandbox_key` returned by the create/connect
      endpoints, used for data-plane host addressing. `{domain}` and
      `X-Access-Token` are the `domain` and `sandbox_access_token` from the same
      responses — no extra endpoint is needed to obtain them.


      This split is not historical baggage but the nature of a data plane: file
      transfer is long-lived, high-volume, and addressed per sandbox, differing
      from the control plane's short requests in both capacity model and failure
      domain, so they do not share an entry point.


      **One unified abstraction.** The same contract is served by two backend
      kinds (an in-sandbox file service / a data-center data-plane proxy);
      clients need not — and cannot — tell them apart. The cost is that the
      contract is their intersection: `Content-Length` on download and resumable
      download (`Range`/`206`) are **optional capabilities** that vary by data
      center, and clients must work correctly when they are absent. See the
      per-operation notes below.
paths:
  /sandboxes/{id}/actions:
    post:
      tags:
        - Sandbox
      summary: Execute a sandbox runtime action
      description: >
        Single dispatch endpoint for runtime actions on a running sandbox.
        Currently `update_egress_policy` is supported: it replaces the egress
        allowlist. Only a sandbox created with `egress_control=true` can be
        updated; the allowlist is a full replacement, not a merge.


        `applied=immediate` means the change is already in effect (existing
        connections are not promised to drop); `applied=eventual` means the
        change is accepted and still propagating. A sandbox without
        `egress_control` returns 400.


        **Only a `running` sandbox accepts this call.** `paused` and other
        transitional states return 409: a paused sandbox has no writable live
        network, and a resume does not replay a change submitted while paused —
        accepting the call would silently do nothing. Resume the sandbox first
        with `POST /sandboxes/{id}/resume`.
      operationId: executeSandboxAction
      parameters:
        - $ref: '#/components/parameters/SandboxIdPathParam'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ExecuteSandboxNetworkActionRequest'
      responses:
        '200':
          description: Action executed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UpdateSandboxEgressPolicyResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/SandboxMutationFailed'
        '422':
          $ref: '#/components/responses/UnprocessableEntity'
        '499':
          $ref: '#/components/responses/RequestCanceled'
        '500':
          $ref: '#/components/responses/SandboxMutationFailed'
        '504':
          $ref: '#/components/responses/RequestTimeout'
components:
  parameters:
    SandboxIdPathParam:
      name: id
      in: path
      required: true
      description: >-
        Unique sandbox resource ID (UUID; the `id` field of the create
        response). The data-plane key (`sandbox_key`) cannot be used here.
      schema:
        type: string
        format: uuid
      example: 3f6f0d3e-6f0a-4a2e-9c58-0d3e6f0a4a2e
  schemas:
    ExecuteSandboxNetworkActionRequest:
      type: object
      required:
        - action
        - params
      additionalProperties: false
      properties:
        action:
          type: string
          enum:
            - update_egress_policy
        params:
          $ref: '#/components/schemas/SandboxEgressPolicy'
    UpdateSandboxEgressPolicyResponse:
      type: object
      required:
        - request_id
        - data
      properties:
        request_id:
          type: string
          description: Request tracing ID
        data:
          $ref: '#/components/schemas/UpdateSandboxEgressPolicyResult'
    SandboxEgressPolicy:
      type: object
      additionalProperties: false
      properties:
        allowed_hostnames:
          type: array
          items:
            type: string
          description: Allowed hostnames; a leading `*.` wildcard is supported.
        allowed_cidrs:
          type: array
          items:
            type: string
          description: Allowed IPv4 CIDRs.
    UpdateSandboxEgressPolicyResult:
      type: object
      required:
        - state
        - last_mutation
        - applied
      properties:
        state:
          $ref: '#/components/schemas/SandboxControlState'
        last_mutation:
          $ref: '#/components/schemas/SandboxMutationResult'
        applied:
          type: string
          enum:
            - immediate
            - eventual
          description: >-
            `immediate`: the change is already in effect; existing connections
            are not promised to drop. `eventual`: the change is accepted and
            still propagating to the running sandbox. Either value means the
            change has been accepted — there is no "saved, takes effect on next
            resume" outcome; a change submitted while paused returns 409
            instead.
    ErrorResponse:
      type: object
      required:
        - code
        - message
        - request_id
      properties:
        code:
          type: string
          description: Machine-readable business error code
          example: Resource.QuotaExceeded
        message:
          type: string
          description: Human-readable error description
          example: The resource quota is not enough.
        request_id:
          type: string
          description: Tracing ID
          example: req-xxxx
        trace_id:
          type: string
          description: >-
            Server-side trace identifier for this request; quote it when
            reporting a problem.
        details:
          type: object
          description: Field-level error details
          additionalProperties: true
    SandboxMutationErrorResponse:
      description: >
        Error response for a failed runtime mutation. The top level keeps the
        platform's standard error envelope (ErrorResponse); the sandbox-specific
        `state` / `last_mutation` go in `details` without altering the top-level
        structure.
      allOf:
        - $ref: '#/components/schemas/ErrorResponse'
        - type: object
          properties:
            details:
              type: object
              properties:
                state:
                  type: string
                  description: >-
                    The sandbox's actual lifecycle state after this operation
                    failed
                last_mutation:
                  $ref: '#/components/schemas/SandboxMutationResult'
    SandboxControlState:
      type: string
      description: >
        External lifecycle state. The internal `creating` state is expressed
        externally as `provisioning` (the instance is being prepared and is not
        usable yet); backend queueing is not expressed separately and is also
        folded into `provisioning`.


        `deleting` / `deleted`: deletion is a soft delete - once accepted the
        instance enters `deleting`, and after the backing resources are
        confirmed released it reaches the terminal `deleted` state, whose record
        is kept permanently. In both states the detail endpoint still returns
        200 and the `deletion` object says who initiated the deletion and why;
        the data plane returns 404 for them, as do PATCH, connect, pause, resume
        and timeout; `/actions` returns 409, or 400 when the sandbox was created
        without `egress_control`. DELETE can be repeated: it returns 202 while
        `deleting` and 200 once `deleted`. The default list excludes both
        states, and `state=deleted` returns deleted records explicitly.


        `pausing` / `paused` / `resuming`: pause and resume are an explicit pair
        of lifecycle operations (see `POST /sandboxes/{id}/pause` and `POST
        /sandboxes/{id}/resume`); both return 202 on acceptance and the settled
        outcome is read back from this field. `pausing` and `resuming` are
        transitional. `paused` means the disk is retained and the instance is no
        longer running, and no longer consuming its runtime budget either
        (`end_at` is frozen and may already be in the past); it is reclaimed at
        the end of the retention window `paused_expire_at`.
      enum:
        - provisioning
        - running
        - pausing
        - paused
        - resuming
        - checkpointing
        - updating
        - failed
        - deleting
        - deleted
    SandboxMutationResult:
      type: object
      description: >
        The most recent operation and its outcome. status=pending means the
        command has been accepted but the outcome has not settled yet
        (asynchronous creation and deletion are in this state right after
        acceptance).
      required:
        - type
        - status
      properties:
        type:
          type: string
          enum:
            - create
            - delete
            - set_timeout
            - update_egress_policy
            - update_ingress_policy
            - rotate_traffic_token
        status:
          type: string
          enum:
            - pending
            - succeeded
            - failed
        error_code:
          type: string
          example: invalid_argument
        message:
          type: string
          description: Sanitized failure reason; never contains access tokens
        completed_at:
          type: string
          format: date-time
  responses:
    BadRequest:
      description: >-
        Invalid request parameters. Two code families are used:
        `Request.InvalidParameter` for request parsing and format validation
        (unknown or malformed body fields, malformed IDs, invalid enum values,
        out-of-range paging) and `Parameter.Invalid` for business validation
        (for example a template or IDC that cannot be found, or a `timeout` over
        the limit). Unknown or invalid fields inside the `params` of `POST
        /sandboxes/{id}/actions` are reported as `Parameter.Invalid`. Other 400
        codes are specific to an operation, for example `Sandbox.SourceConflict`
        (both or neither of `template_id` and `snapshot_id`) and
        `Sandbox.IdempotentParameterMismatch` (an `Idempotency-Key` reused with
        different parameters). Both code families share the same HTTP status, so
        decide by the `code` value, not the status alone.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            code: Parameter.Invalid
            message: Invalid request parameters
            request_id: req-xxxx
    Unauthorized:
      description: Unauthenticated
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            code: Auth.Unauthorized
            message: Authentication required
            request_id: req-xxxx
    Forbidden:
      description: The caller cannot access the organization-scoped resource.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    NotFound:
      description: Resource not found
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            code: Resource.NotFound
            message: The requested resource was not found
            request_id: req-xxxx
    SandboxMutationFailed:
      description: >
        A runtime mutation started but was deterministically rejected by the
        backing infrastructure. The top level is the platform's standard error
        envelope; `details` additionally carries `state` and `last_mutation`,
        indicating the sandbox is still running while this operation failed.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/SandboxMutationErrorResponse'
          example:
            code: Resource.StateConflict
            message: 'Sandbox state conflict: current updating, required running'
            request_id: req-xxxx
            details:
              state: running
              last_mutation:
                type: set_timeout
                status: failed
                error_code: invalid_argument
                message: timeout update rejected by runtime
                completed_at: '2026-08-13T09:12:31Z'
    UnprocessableEntity:
      description: >-
        The request is valid, but the requested capability is not supported in
        the selected environment.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            code: Template.ProviderNotSupported
            message: The selected IDC does not support this template operation
            request_id: req-xxxx
    RequestCanceled:
      description: The caller canceled the current HTTP request.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            code: Request.Canceled
            message: Request canceled
            request_id: req-xxxx
    RequestTimeout:
      description: The current HTTP request exceeded the control-plane processing deadline.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            code: Request.Timeout
            message: Request timed out
            request_id: req-xxxx
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: >
        API Key or Access Token, always sent as `Authorization: Bearer <token>`.
        The gateway recognizes the token type automatically.

````

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