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

# Attach to the sandbox shell (WebSocket)

> WebSocket handshake — **not a normal GET**. Code generators produce no
usable client for this operation (see `x-websocket`); consumers use a
WebSocket library instead. The address is composed from the `POST /shell`
response: `wss://{sandbox_key}.{domain}{connect_url}` — the same
data-plane host, scheme `wss`.

The client sends the standard upgrade headers (`Upgrade: websocket`,
`Connection: Upgrade`, plus the `Sec-WebSocket-*` headers WebSocket
libraries add automatically) together with the same `X-Access-Token`
used by the other data-plane endpoints. When a browser sends an `Origin`
header, it must be on the server allowlist, otherwise the handshake is
refused with `403`.

After the `101` upgrade the connection switches to WebSocket framing:

- **BINARY frames only.** client→server frames carry raw terminal input
  bytes; server→client frames carry raw terminal output bytes (ANSI
  escape sequences included, xterm.js-compatible).
- Text frames are a protocol violation: the server drops the connection.
- **No JSON messages in-band.** Resize and close go through
  `POST /shell/control`, never over the WebSocket.
- At most **one active connection per sandbox**; a second concurrent
  attach is refused at handshake time with `409`.
- Disconnecting keeps the shell alive — reattaching resumes the same
  session. Only `POST /shell/control` with `action=close` destroys it.

Pre-upgrade errors are returned as plain HTTP responses (see below).




## OpenAPI

````yaml /api-spec/sandbox_api.yaml get /shell/connect
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/connect:
    get:
      tags:
        - Sandbox-Shell
      summary: Attach to the sandbox shell (WebSocket)
      description: >
        WebSocket handshake — **not a normal GET**. Code generators produce no

        usable client for this operation (see `x-websocket`); consumers use a

        WebSocket library instead. The address is composed from the `POST
        /shell`

        response: `wss://{sandbox_key}.{domain}{connect_url}` — the same

        data-plane host, scheme `wss`.


        The client sends the standard upgrade headers (`Upgrade: websocket`,

        `Connection: Upgrade`, plus the `Sec-WebSocket-*` headers WebSocket

        libraries add automatically) together with the same `X-Access-Token`

        used by the other data-plane endpoints. When a browser sends an `Origin`

        header, it must be on the server allowlist, otherwise the handshake is

        refused with `403`.


        After the `101` upgrade the connection switches to WebSocket framing:


        - **BINARY frames only.** client→server frames carry raw terminal input
          bytes; server→client frames carry raw terminal output bytes (ANSI
          escape sequences included, xterm.js-compatible).
        - Text frames are a protocol violation: the server drops the connection.

        - **No JSON messages in-band.** Resize and close go through
          `POST /shell/control`, never over the WebSocket.
        - At most **one active connection per sandbox**; a second concurrent
          attach is refused at handshake time with `409`.
        - Disconnecting keeps the shell alive — reattaching resumes the same
          session. Only `POST /shell/control` with `action=close` destroys it.

        Pre-upgrade errors are returned as plain HTTP responses (see below).
      operationId: attachSandboxShell
      responses:
        '101':
          description: >
            Handshake succeeded. The connection is now a WebSocket carrying raw

            terminal bytes in BINARY frames; see the operation description for

            the framing protocol. If attaching to the shell fails after the
            upgrade,

            the server closes the WebSocket with close code `1011` and reason

            `attach failed`.
        '400':
          description: >
            `Shell.InvalidRequest`: required WebSocket upgrade headers

            (`Upgrade: websocket`, `Connection: Upgrade`) are missing or
            malformed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SandboxShellErrorResponse'
        '401':
          $ref: '#/components/responses/SandboxShellUnauthorized'
        '403':
          description: >
            An `Origin` header was present but is not on the server allowlist

            (browser clients only; non-browser clients normally send no
            `Origin`). The error code is `Shell.InvalidRequest`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SandboxShellErrorResponse'
        '404':
          $ref: '#/components/responses/SandboxShellNotFound'
        '409':
          description: >
            Attach conflict, one of:

            - `Shell.AlreadyAttached`: another WebSocket connection is already
              active (at most one per sandbox).
            - `Shell.Creating`: a launch is still in progress; retry shortly.

            - `Shell.Closing`: the shell is being torn down; launch a new one
              with `POST /shell` afterwards.
            - `Shell.CreateFailed`: the previous launch failed; launch it again
            with
              `POST /shell`.
            - `Shell.RuntimeNotRunning`: the sandbox is not running.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SandboxShellErrorResponse'
              example:
                code: Shell.AlreadyAttached
                message: shell already has an active connection
                request_id: req-7f3c
        '500':
          description: |
            `Shell.InternalError`: unexpected server-side failure.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SandboxShellErrorResponse'
        '503':
          description: |
            Temporarily unavailable, one of `Shell.StoreUnavailable` or
            `Shell.ProviderUnavailable`. Safe to retry.
          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:
  schemas:
    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
  responses:
    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
    SandboxShellNotFound:
      description: |
        No shell session exists (`Shell.NotFound`): none has been launched yet —
        call `POST /shell` first — or the shell has already been closed.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/SandboxShellErrorResponse'
          example:
            code: Shell.NotFound
            message: no shell session for this sandbox
            request_id: req-7f3c
  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.