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

# Launch the sandbox interactive shell

> Ensures the sandbox's single logical shell (PTY) session exists. The call is
idempotent: if the shell already exists, it returns `200` with the current
status instead of creating a duplicate. The effective URL is the sandbox
data-plane host (`https://{sandbox_key}.{domain}`; both values, like the
`X-Access-Token` value, come from the control-plane create/connect response).
The endpoint does not include the control-plane `/api/v2` prefix.

The request body must be empty or `{}`; unknown fields are rejected with
`400 Shell.InvalidRequest`.

On `200`, `connect_url` is the **relative** path of the WebSocket attach
endpoint. The full attach address is
`wss://{sandbox_key}.{domain}{connect_url}` — the same host this request was
sent to, with the scheme switched to `wss`.

A `202` with `code=Shell.Creating` means another request is currently
creating the shell; retry `POST /shell` shortly — the retry never creates a
duplicate PTY.




## OpenAPI

````yaml /api-spec/sandbox_api.yaml post /shell
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:
  /shell:
    post:
      tags:
        - Sandbox-Shell
      summary: Launch the sandbox interactive shell
      description: >
        Ensures the sandbox's single logical shell (PTY) session exists. The
        call is

        idempotent: if the shell already exists, it returns `200` with the
        current

        status instead of creating a duplicate. The effective URL is the sandbox

        data-plane host (`https://{sandbox_key}.{domain}`; both values, like the

        `X-Access-Token` value, come from the control-plane create/connect
        response).

        The endpoint does not include the control-plane `/api/v2` prefix.


        The request body must be empty or `{}`; unknown fields are rejected with

        `400 Shell.InvalidRequest`.


        On `200`, `connect_url` is the **relative** path of the WebSocket attach

        endpoint. The full attach address is

        `wss://{sandbox_key}.{domain}{connect_url}` — the same host this request
        was

        sent to, with the scheme switched to `wss`.


        A `202` with `code=Shell.Creating` means another request is currently

        creating the shell; retry `POST /shell` shortly — the retry never
        creates a

        duplicate PTY.
      operationId: launchSandboxShell
      parameters:
        - $ref: '#/components/parameters/SandboxShellRequestIDParam'
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              additionalProperties: false
              description: >
                Empty object. Any unknown field is rejected with `400
                Shell.InvalidRequest`.
            example: {}
      responses:
        '200':
          description: |
            The shell exists (created by this call, or already present).
            `status=ready` means the PTY exists and can be attached;
            `status=attached` means the shell exists **and** currently has an
            active WebSocket connection (a second attach would get `409`).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SandboxShellLaunchResponse'
              example:
                request_id: req-7f3c
                sandbox_key: sandbox-key
                status: ready
                connect_url: /shell/connect
        '202':
          description: |
            Another request is currently creating the shell (`Shell.Creating`).
            Retry `POST /shell` shortly; the launch is idempotent and a retry
            never creates a duplicate PTY.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SandboxShellCreatingResponse'
              example:
                request_id: req-7f3c
                sandbox_key: sandbox-key
                status: creating
                code: Shell.Creating
        '400':
          $ref: '#/components/responses/SandboxShellBadRequest'
        '401':
          $ref: '#/components/responses/SandboxShellUnauthorized'
        '409':
          description: >
            State conflict, one of:

            - `Shell.RuntimeNotRunning`: the sandbox is not running (for example
            it is
              paused and cannot be resumed right now, or it is still changing state).
            - `Shell.Closing`: the shell is being torn down; retry `POST /shell`
            shortly.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SandboxShellErrorResponse'
        '500':
          description: |
            `Shell.InternalError`: unexpected server-side failure.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SandboxShellErrorResponse'
        '502':
          description: >
            `Shell.CreateFailed`: the shell could not be created in the selected
            IDC.

            Retrying later starts a fresh session.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SandboxShellErrorResponse'
        '503':
          description: >
            `Shell.ProviderUnavailable` or `Shell.StoreUnavailable`: the shell
            state could not be

            determined. Safe to retry — a retry never creates a duplicate PTY.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SandboxShellErrorResponse'
      security:
        - sandboxAccessToken: []
      servers:
        - url: https://{sandbox_key}.{domain}
          description: Sandbox data-plane host
          variables:
            sandbox_key:
              default: sandbox-key
              description: >-
                Data-plane addressing key (`sandbox_key` from the control-plane
                response).
            domain:
              default: sandbox.example.com
              description: Sandbox data-plane domain from the control-plane response.
components:
  parameters:
    SandboxShellRequestIDParam:
      name: X-Request-ID
      in: header
      required: false
      description: >
        Ignored. The server generates its own request ID (`req-<uuid>`) for
        every request and

        returns it in the `X-Request-ID` response header and the body's
        `request_id`.
      schema:
        type: string
        minLength: 1
        maxLength: 128
  schemas:
    SandboxShellLaunchResponse:
      type: object
      required:
        - request_id
        - sandbox_key
        - status
        - connect_url
      properties:
        request_id:
          type: string
          description: >-
            Server-generated request ID (`req-<uuid>`), identical to the
            `X-Request-ID` response header.
        sandbox_key:
          type: string
          description: >-
            Echo of the data-plane addressing key (i.e. `sandbox_key`, the
            leftmost label of the request host).
        status:
          $ref: '#/components/schemas/SandboxShellStatus'
        connect_url:
          type: string
          description: |
            **Relative** path of the WebSocket attach endpoint. The full attach
            address is `wss://{sandbox_key}.{domain}{connect_url}` — the same
            data-plane host this request was sent to, with the scheme switched
            to `wss`. This is always the sandbox's own data-plane endpoint.
          example: /shell/connect
    SandboxShellCreatingResponse:
      type: object
      required:
        - request_id
        - sandbox_key
        - status
        - code
      properties:
        request_id:
          type: string
          description: >-
            Server-generated request ID (`req-<uuid>`), identical to the
            `X-Request-ID` response header.
        sandbox_key:
          type: string
          description: >-
            Echo of the data-plane addressing key (i.e. `sandbox_key`, the
            leftmost label of the request host).
        status:
          type: string
          enum:
            - creating
          description: Always `creating` on this response.
        code:
          type: string
          enum:
            - Shell.Creating
          description: |
            Stable machine-readable hint that another request is currently
            creating the shell. Retry `POST /shell` shortly; the retry never
            creates a duplicate PTY.
    SandboxShellErrorResponse:
      type: object
      description: >-
        Data-plane error body, in the platform error envelope (without
        `trace_id`).
      required:
        - code
        - message
        - request_id
      properties:
        code:
          type: string
          description: >-
            Machine-readable error code (`Shell.*`; a 401 is always
            `Auth.Unauthorized`). The set of codes and their meanings are
            documented on each response.
          example: Shell.NotFound
        message:
          type: string
          description: Stable, sanitized human-readable error message.
        request_id:
          type: string
          description: >-
            Server-generated ID for this request (`req-<uuid>`), identical to
            the `X-Request-ID` response header. A caller-supplied `X-Request-ID`
            is not used.
          example: req-3f2b6c1e-8a0d-4f6b-9c3e-2d7a1b5e9f40
    SandboxShellStatus:
      type: string
      description: |
        Public status of the sandbox shell session.
        - `creating`: a launch is in progress (only appears with `202`).
        - `ready`: the PTY exists and can be attached.
        - `attached`: the shell exists and currently has an active WebSocket
          connection (a second attach would get `409`).
        - `closed`: the shell has been terminated by `action=close` and can
          never be re-attached.
      enum:
        - creating
        - ready
        - attached
        - closed
  responses:
    SandboxShellBadRequest:
      description: |
        Invalid request (`Shell.InvalidRequest`): the body contains unknown or
        extra fields, an unknown `action`, or out-of-bounds values
        (e.g. `cols`/`rows` outside 1..1000).
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/SandboxShellErrorResponse'
          example:
            code: Shell.InvalidRequest
            message: request body contains unknown field
            request_id: req-7f3c
    SandboxShellUnauthorized:
      description: >
        Missing or invalid `X-Access-Token`, as on the other data-plane
        endpoints. The body is always

        `{"code":"Auth.Unauthorized","message":"unauthorized","request_id":"..."}`;
        missing, malformed,

        expired, and wrong-sandbox tokens are indistinguishable.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/SandboxShellErrorResponse'
          example:
            code: Auth.Unauthorized
            message: unauthorized
            request_id: req-3f2b6c1e-8a0d-4f6b-9c3e-2d7a1b5e9f40
  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.
    sandboxAccessToken:
      type: apiKey
      in: header
      name: X-Access-Token
      description: >
        Sandbox data-plane token — the `sandbox_access_token` returned by the
        create/connect endpoints. Used for `/files`, Sandbox-Exec, and other
        data-plane endpoints; the control plane (`/api/v2`) does not accept it,
        and conversely the control plane's `Authorization: Bearer` is invalid on
        the data plane.


        The token is bound to its sandbox: using sandbox A's token against
        sandbox B's host fails. It stays valid until the sandbox's lifetime
        ends; there is no separate rotation endpoint (`rotate_traffic_token`
        rotates the port traffic token `traffic_access_token` and does not
        affect this one).

````

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