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

# Update sandbox metadata

> Replaces the customer-defined metadata as a whole — no per-key merging; passing an empty object clears it. There are no mutable fields other than `metadata`. This operation does not take the lifecycle lock, so it works in any visible state.




## OpenAPI

````yaml /api-spec/sandbox_api.yaml patch /sandboxes/{id}
openapi: 3.0.3
info:
  title: GMI Sandbox API
  description: >-
    REST API for GMI Sandbox: create isolated execution environments from
    templates, 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: 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-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}:
    patch:
      tags:
        - Sandbox
      summary: Update sandbox metadata
      description: >
        Replaces the customer-defined metadata as a whole — no per-key merging;
        passing an empty object clears it. There are no mutable fields other
        than `metadata`. This operation does not take the lifecycle lock, so it
        works in any visible state.
      operationId: updateSandbox
      parameters:
        - $ref: '#/components/parameters/SandboxIdPathParam'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateSandboxControlRequest'
      responses:
        '200':
          description: Update succeeded
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SandboxControlDetailResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '500':
          $ref: '#/components/responses/InternalError'
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:
    UpdateSandboxControlRequest:
      type: object
      required:
        - metadata
      properties:
        metadata:
          type: object
          additionalProperties:
            type: string
          description: Customer metadata, replaced as a whole.
    SandboxControlDetailResponse:
      type: object
      required:
        - request_id
        - data
      properties:
        request_id:
          type: string
          description: Request tracing ID
        data:
          $ref: '#/components/schemas/SandboxControlDetail'
    SandboxControlDetail:
      type: object
      properties:
        id:
          type: string
          format: uuid
          description: >-
            Unique sandbox resource ID (UUID), used both in control-plane path
            parameters and as the billing resource_id.
        sandbox_key:
          type: string
          description: >
            Data-plane addressing key (an `i` followed by a 20-character random
            string). Used solely to assemble data-plane URLs:
            `https://{sandbox_key}.{domain}` and
            `https://{port}-{sandbox_key}.{domain}`. Control-plane endpoints
            always use `id` and do not accept this field.
        template_id:
          type: string
        idc_name:
          type: string
        state:
          $ref: '#/components/schemas/SandboxControlState'
        product:
          type: string
          description: Catalog SKU when created from a preset; empty for custom resources.
        product_name:
          type: string
          description: Preset label when created from a preset; empty for custom resources.
        resources:
          $ref: '#/components/schemas/SandboxResources'
        metadata:
          type: object
          additionalProperties:
            type: string
          description: >-
            Customer-defined key/value pairs; the platform does not interpret
            them. The list endpoint can filter by them.
        latest_operation:
          $ref: '#/components/schemas/SandboxMutationResult'
        failure:
          $ref: '#/components/schemas/SandboxFailure'
        created_at:
          type: string
          description: Time the request was accepted, UTC
        running_at:
          type: string
          description: >-
            Time the instance was confirmed running; empty string while not yet
            running
        started_at:
          type: string
          description: UTC timestamp, e.g. 2026-08-04T09:12:31.123456789Z
        end_at:
          type: string
          description: UTC timestamp; the expiry time
        domain:
          type: string
    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
        details:
          type: object
          description: Field-level error details
          additionalProperties: true
    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` is not listed here: once deletion is accepted the instance is
        no longer queryable (the detail endpoint returns 404), and that state
        appears only in the deletion endpoint's acceptance response.
        Suspend-related states likewise never appear — suspend is not offered
        externally.
      enum:
        - provisioning
        - running
        - checkpointing
        - updating
        - failed
    SandboxResources:
      type: object
      description: >-
        Compute resources. Fixed when the Template Build is created and
        snapshotted at sandbox creation; immutable afterwards, and the create
        request cannot override them.
      properties:
        vcpu:
          type: integer
          format: int64
        memory_mb:
          type: integer
          format: int64
        disk_mb:
          type: integer
          format: int64
    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
        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
    SandboxFailure:
      type: object
      description: >-
        Non-empty only when state=failed. The message is sanitized and contains
        no backend identifiers or internal host addresses.
      properties:
        code:
          type: string
          example: Parameter.Invalid
        message:
          type: string
        occurred_at:
          type: string
  responses:
    BadRequest:
      description: Invalid request parameters
      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
    InternalError:
      description: Internal server error
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            code: Internal.ServerError
            message: An internal server error occurred
            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.

````