GPU Cloud Servers API

The Servers API from GPU Cloud — 4 operation(s) for servers.

GitHub OpenAPI

Operations 6

GET /servers List Servers #
POST /servers Create Server #
GET /servers/{id} Get Server #
DELETE /servers/{id} Delete Server #
POST /servers/{id}/actions Server Action #
PUT /servers/{id}/threshold-action Set the action at the threshold #

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/gpucloud-ca:gpucloud-ca-servers-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Calls without X-API-Key need no key. Calls with it need a paid plan — get a key.

Get an API key

Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

gpucloud-ca-servers-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: GPU Cloud Servers API
  description: 'REST API of GPUcloud.ca: GPU plans, GPU servers (deploy, power actions, metrics, firewall), SSH keys and API keys. All prices are in CAD. Authenticate with a REST API key: `Authorization: Bearer `. AI agents should use the MCP endpoint (https://gpucloud.ca/api/mcp) with an MCP key instead. REST keys (prefix gpu_) are created in the console, expire after 90 days and are either full access or read-only (GET only; a read-only key gets 401 on any other method). Write requests (POST, PUT, DELETE) must also send the header `Origin: https://gpucloud.ca`, otherwise they get 403. Developer guide: https://gpucloud.ca/en/developers'
  version: 1.1.1
  contact:
    name: GPU Cloud Support
    url: https://gpucloud.ca
    email: info@gpucloud.ca
servers:
- url: https://gpucloud.ca/api
  description: Production
security: []
tags:
- name: Servers
paths:
  /servers:
    get:
      summary: List Servers
      description: List all GPU servers owned by the authenticated user.
      operationId: listServers
      tags:
      - Servers
      security:
      - BearerAuth: []
      responses:
        '200':
          description: List of servers
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/ServerSummary'
              example:
                data:
                - id: clxyz123456
                  name: gpu-abc12345-1719000000000
                  status: ACTIVE
                  gpu: L40
                  specs:
                    vcores: 28
                    ram: 58
                    storage: 100
                  ipv4: 1.2.3.4
                  region: canada-montreal
                  monthlyCost: 1131.5
                  createdAt: '2026-06-24T12:00:00.000Z'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalError'
    post:
      summary: Create Server
      description: 'Deploy a new GPU server. A public IP address is assigned once the server is running. The first hour (hourly billing) is taken from the credit balance. Neither a saved card nor the automatic reload is required: the only requirement is paying the first hour. onFundsExhausted chooses what happens at the threshold; when the automatic reload is off, the answer carries autoReloadWarning.'
      operationId: createServer
      tags:
      - Servers
      security:
      - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateServerRequest'
            example:
              productId: gpu-l40
              sshKeyId: clxyz123456
              name: my-ml-server
              region: canada-montreal
      responses:
        '201':
          description: Server created and provisioning started
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: '#/components/schemas/ServerCreated'
                  autoReloadWarning:
                    $ref: '#/components/schemas/AutoReloadWarning'
              example:
                data:
                  id: clxyz123456
                  name: gpu-abc12345-1719000000000
                  status: PROVISIONING
                  gpu: L40
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '402':
          $ref: '#/components/responses/PaymentRequired'
        '409':
          description: GPU not available in the requested region
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                error: GPU not currently available in this region
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalError'
  /servers/{id}:
    parameters:
    - name: id
      in: path
      required: true
      schema:
        type: string
      description: Server ID
    get:
      summary: Get Server
      description: Get full details for a specific server.
      operationId: getServer
      tags:
      - Servers
      security:
      - BearerAuth: []
      responses:
        '200':
          description: Server details
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: '#/components/schemas/ServerDetail'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalError'
    delete:
      summary: Delete Server
      description: Destroy a GPU server and all its data (irreversible).
      operationId: deleteServer
      tags:
      - Servers
      security:
      - BearerAuth: []
      responses:
        '200':
          description: Server deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: string
                      status:
                        type: string
                        example: DELETED
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalError'
  /servers/{id}/actions:
    parameters:
    - name: id
      in: path
      required: true
      schema:
        type: string
      description: Server ID
    post:
      summary: Server Action
      description: 'Execute a power action on the server: start, stop (a stopped server is still billed), reboot, hibernate (then billed its much lower hibernation rate, returned as hibernationFee with the message to show; public IP released; delete the server to stop paying entirely; an hourly server you hibernate is billed its hibernation fee and kept as long as the account has credit left; only once the credit is exhausted is it deleted with its data, after three notices (the credit is near its end, then about 24 hours and 1 hour before the deletion) and never within 72 hours of the hibernation; adding credits cancels the deletion; returned as customerHibernation with maxHours (the minimum hours before any deletion), deleteAt (always null: no deletion date while credit remains) and the message to show) or hibernate-restore (a new public IP is assigned).'
      operationId: serverAction
      tags:
      - Servers
      security:
      - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ActionRequest'
            example:
              action: reboot
      responses:
        '200':
          description: Action accepted
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: string
                      action:
                        type: string
                      status:
                        type: string
                        example: accepted
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '402':
          $ref: '#/components/responses/PaymentRequired'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalError'
  /servers/{id}/threshold-action:
    put:
      summary: Set the action at the threshold
      description: 'What happens to this hourly server when the balance reaches its threshold and no automatic reload brings it back: HIBERNATE or DELETE. A machine that cannot be hibernated (spot) only accepts DELETE (422 HIBERNATION_NOT_SUPPORTED with the reason). A prepaid server has no threshold (409 NOT_HOURLY).'
      operationId: setServerThresholdAction
      tags:
      - Servers
      security:
      - BearerAuth: []
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - onFundsExhausted
              properties:
                onFundsExhausted:
                  type: string
                  enum:
                  - HIBERNATE
                  - DELETE
            example:
              onFundsExhausted: DELETE
      responses:
        '200':
          description: The effective action and the threshold
          content:
            application/json:
              schema:
                type: object
                properties:
                  serverId:
                    type: string
                  onFundsExhausted:
                    type: string
                    enum:
                    - HIBERNATE
                    - DELETE
                  hibernationThresholdCad:
                    type: number
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: 'Prepaid server: no threshold'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                error: NOT_HOURLY
        '422':
          description: 'This machine cannot be hibernated: DELETE only'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                error: HIBERNATION_NOT_SUPPORTED
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalError'
components:
  schemas:
    FundsExhaustedAction:
      type: string
      enum:
      - HIBERNATE
      - DELETE
      default: HIBERNATE
      description: 'What happens to this hourly server when the balance reaches its threshold and no automatic reload brings it back. HIBERNATE: hibernated (its GPU place is kept, hourly hibernation fee), restarted as soon as the balance is back above the threshold. DELETE: no hibernation fee; it keeps running while the balance pays its next hour, then it is taken out of service, and restarted as soon as the balance pays its next hour again. A server is never deleted while the account has credit left. When the balance covers only about 24 hours of what the account spends per hour, a first notice is sent while the servers still run, then two more about 24 hours and 1 hour before the deletion; a server is deleted with its data only once it is out of service (stopped or hibernated for lack of credit, or hibernated by the user for at least 72 hours), once the credit is exhausted (the balance no longer pays the next hour of the account''s servers), after the three notices and at least 24 hours after the first. Adding credits cancels the deletion. Machines that cannot be hibernated (spot) are always DELETE. Any other value: 400.'
    Error:
      type: object
      properties:
        error:
          type: string
      required:
      - error
    AccountAction:
      type: object
      description: What to do to fix an account or payment problem, with the direct absolute link. Same object on every route and in the MCP tool errors (structuredContent.error.accountAction). The message is in French when Accept-Language asks for it, otherwise in English.
      properties:
        code:
          type: string
          enum:
          - NO_API_KEY
          - INVALID_API_KEY
          - PAYMENT_REQUIRED
          - BUDGET_CAP_REACHED
          description: Stable machine code. NO_API_KEY and INVALID_API_KEY come with HTTP 401, PAYMENT_REQUIRED and BUDGET_CAP_REACHED with HTTP 402.
        message:
          type: string
          description: Clear sentence telling what to do, including the link.
        action:
          type: object
          properties:
            label:
              type: string
            url:
              type: string
              format: uri
          required:
          - label
          - url
        links:
          type: object
          description: 'Absolute links: account creation, sign in, API keys page, billing, add credits (opens the credit window), payment card, budget or cap setting, documentation.'
          properties:
            signup:
              type: string
              format: uri
            login:
              type: string
              format: uri
            apiKeys:
              type: string
              format: uri
            billing:
              type: string
              format: uri
            addCredits:
              type: string
              format: uri
            paymentMethod:
              type: string
              format: uri
            budget:
              type: string
              format: uri
            documentation:
              type: string
              format: uri
          required:
          - signup
          - login
          - apiKeys
          - billing
          - addCredits
          - paymentMethod
          - budget
          - documentation
        minimumCad:
          type: number
          description: 'PAYMENT_REQUIRED only, when known: credits (CAD) covering one hour of the GPU asked for, computed by the server from the same price as the deployment.'
        minimumTopupCad:
          type: number
          description: Smallest credit purchase accepted (CAD), given with minimumCad.
        balanceCad:
          type: number
          description: 'PAYMENT_REQUIRED and BUDGET_CAP_REACHED, when known: the account''s credit balance (CAD) when the request was refused.'
        hibernationThresholdCad:
          type: number
          description: 'PAYMENT_REQUIRED, when a server is concerned: its hibernation threshold (CAD), the larger of 3 CAD and 2 hours of its hourly rate. A running hourly server is hibernated when the balance reaches its threshold (after the automatic reload was tried); it restarts automatically as soon as the balance is above the threshold. A server is never deleted while the account has credit left. When the balance covers only about 24 hours of what the account spends per hour, a first notice is sent while the servers still run, then two more about 24 hours and 1 hour before the deletion; a server is deleted with its data only once it is out of service (stopped or hibernated for lack of credit, or hibernated by the user for at least 72 hours), once the credit is exhausted (the balance no longer pays the next hour of the account''s servers), after the three notices and at least 24 hours after the first. Adding credits cancels the deletion. For the restart of such a server, minimumCad is the balance that restarts it (threshold + 0.01).'
        capCad:
          type: number
          description: 'BUDGET_CAP_REACHED of an MCP key, for a money cap: the cap reached (CAD). The cap set by the customer is always respected: nothing is charged or reloaded beyond it.'
        capRemainingCad:
          type: number
          description: 'BUDGET_CAP_REACHED of an MCP key, with capCad: what is left of that cap (CAD).'
      required:
      - code
      - message
      - action
      - links
    ServerCreated:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
        status:
          type: string
          example: PROVISIONING
        gpu:
          type: string
        onFundsExhausted:
          type:
          - string
          - 'null'
          enum:
          - HIBERNATE
          - DELETE
          - null
          description: Effective action at the threshold (hourly servers), null for a prepaid server.
    AutoReloadWarning:
      type:
      - object
      - 'null'
      description: 'Present only when the automatic reload is OFF: show the message to the user. Never a payment link.'
      properties:
        code:
          type: string
          enum:
          - AUTO_RELOAD_OFF
        action:
          type: string
          enum:
          - HIBERNATE
          - DELETE
        thresholdCad:
          type: number
          description: 'Threshold of the server (CAD): the larger of 3 CAD and 2 hours of its rate.'
        message:
          type: string
          example: Without automatic reload, this server will be hibernated when your balance reaches $3.00 (its threshold). It will be deleted only if your credit runs out, after three e-mail notices. For an overnight job, turn on the automatic reload.
        autoReloadSettingsUrl:
          type: string
          format: uri
          description: Console page where the automatic reload is turned on.
    ServerSummary:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
        status:
          type: string
          enum:
          - PROVISIONING
          - ACTIVE
          - STOPPED
          - REBOOTING
          - HIBERNATED
          - ERROR
          - DELETED
        gpu:
          type: string
        specs:
          type: object
          properties:
            vcores:
              type: integer
            ram:
              type: integer
            storage:
              type: integer
        ipv4:
          type:
          - string
          - 'null'
        region:
          type: string
        monthlyCost:
          type: number
        createdAt:
          type: string
          format: date-time
    AccountActionError:
      type: object
      description: 'Error of an account or payment problem. Backward compatible: `error` keeps its previous string value (for example "Unauthorized" or "INSUFFICIENT_CREDITS"), `message` and the existing details stay; `accountAction` is added.'
      properties:
        error:
          type: string
          description: Previous error string or code, unchanged (backward compatibility).
        message:
          type: string
        requiredCredits:
          type: number
          description: 'INSUFFICIENT_CREDITS: credits (CAD) the action needs.'
        currentBalance:
          type: number
        accountAction:
          $ref: '#/components/schemas/AccountAction'
      required:
      - error
      - accountAction
    CreateServerRequest:
      type: object
      required:
      - productId
      - sshKeyId
      properties:
        productId:
          type: string
          description: GPU plan id (GET /v1/gpu), e.g. gpu-l40
          example: gpu-l40
        sshKeyId:
          type: string
          description: SSH key id (GET /settings/ssh-keys)
        name:
          type: string
          maxLength: 100
          description: Optional server name
        region:
          type: string
          enum:
          - canada-montreal
          default: canada-montreal
          description: Deployment region
        onFundsExhausted:
          $ref: '#/components/schemas/FundsExhaustedAction'
    ActionRequest:
      type: object
      required:
      - action
      properties:
        action:
          type: string
          enum:
          - start
          - stop
          - reboot
          - hibernate
          - hibernate-restore
          description: The power action to execute
    ServerDetail:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
        status:
          type: string
        gpu:
          type: string
        specs:
          type: object
          properties:
            vcores:
              type: integer
            ram:
              type: integer
            storage:
              type: integer
        ipv4:
          type:
          - string
          - 'null'
        ipv6:
          type:
          - string
          - 'null'
        region:
          type: string
        monthlyCost:
          type: number
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
        onFundsExhausted:
          type:
          - string
          - 'null'
          enum:
          - HIBERNATE
          - DELETE
          - null
          description: Effective action at the threshold (hourly servers), null for a prepaid server. Change it with PUT /servers/{id}/threshold-action.
  responses:
    PaymentRequired:
      description: 'Payment required (accountAction.code PAYMENT_REQUIRED: not enough credit, no saved card or a declined card) or a budget cap reached (BUDGET_CAP_REACHED: account or server budget limit, nothing charged). accountAction.balanceCad gives the balance and, when a server is concerned, accountAction.hibernationThresholdCad its hibernation threshold (a server is hibernated when the balance reaches it and restarts as soon as the balance is above it; it is deleted only once the account''s credit is exhausted, after three notices). Add your card and credits at accountAction.action.url (or raise the cap), then send the same request again.'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/AccountActionError'
          examples:
            insufficientCredits:
              summary: Not enough credit to deploy
              value:
                error: INSUFFICIENT_CREDITS
                message: Payment required. You need a paid order or at least enough credits for 1 hour of usage.
                requiredCredits: 1.55
                currentBalance: 0
                accountAction:
                  code: PAYMENT_REQUIRED
                  message: 'Payment required. Add your card and credits here, then run the same request again: you will get your GPU. https://gpucloud.ca/en/console/billing?addCredits=25 At least 1.55 CAD is needed to cover one hour of this GPU (smallest credit purchase: 25 CAD).'
                  action:
                    label: Add a card and credits
                    url: https://gpucloud.ca/en/console/billing?addCredits=25
                  links:
                    signup: https://gpucloud.ca/en/auth/register
                    login: https://gpucloud.ca/en/auth/login
                    apiKeys: https://gpucloud.ca/en/console/settings#api-keys
                    billing: https://gpucloud.ca/en/console/billing
                    addCredits: https://gpucloud.ca/en/console/billing?addCredits=25
                    paymentMethod: https://gpucloud.ca/en/console/billing?addCredits=25#payment-method
                    budget: https://gpucloud.ca/en/console/billing#budget-limit
                    documentation: https://gpucloud.ca/en/developers#errors
                  minimumCad: 1.55
                  minimumTopupCad: 25
            budgetCap:
              summary: Account budget limit reached
              value:
                error: BUDGET_LIMIT_REACHED
                message: The monthly budget limit of this account is reached.
                scope: account
                accountAction:
                  code: BUDGET_CAP_REACHED
                  message: 'The budget limit of this account was reached: nothing was charged. The account owner can raise or remove this limit here, then run the same request again: https://gpucloud.ca/en/console/billing#budget-limit'
                  action:
                    label: Change the limit
                    url: https://gpucloud.ca/en/console/billing#budget-limit
                  links:
                    signup: https://gpucloud.ca/en/auth/register
                    login: https://gpucloud.ca/en/auth/login
                    apiKeys: https://gpucloud.ca/en/console/settings#api-keys
                    billing: https://gpucloud.ca/en/console/billing
                    addCredits: https://gpucloud.ca/en/console/billing?addCredits=25
                    paymentMethod: https://gpucloud.ca/en/console/billing?addCredits=25#payment-method
                    budget: https://gpucloud.ca/en/console/billing#budget-limit
                    documentation: https://gpucloud.ca/en/developers#errors
    NotFound:
      description: Resource not found
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error: Server not found
    TooManyRequests:
      description: Rate limit exceeded
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error: Too many requests
    Unauthorized:
      description: 'No API key (accountAction.code NO_API_KEY) or an invalid, expired, revoked or read only key on a write (INVALID_API_KEY). Provide a valid API key as `Authorization: Bearer <key>`; accountAction gives the links to create an account and a key.'
      headers:
        WWW-Authenticate:
          schema:
            type: string
          description: Bearer realm="gpucloud" (error="invalid_token" when a key was sent).
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/AccountActionError'
          examples:
            noKey:
              summary: No key sent
              value:
                error: Unauthorized. Use Bearer API key.
                message: 'No API key was sent. Create a GPU Cloud account (https://gpucloud.ca/en/auth/register), then an API key in the console (https://gpucloud.ca/en/console/settings#api-keys), and send it in the header Authorization: Bearer gpu_...'
                accountAction:
                  code: NO_API_KEY
                  message: 'No API key was sent. Create a GPU Cloud account (https://gpucloud.ca/en/auth/register), then an API key in the console (https://gpucloud.ca/en/console/settings#api-keys), and send it in the header Authorization: Bearer gpu_...'
                  action:
                    label: Create an account
                    url: https://gpucloud.ca/en/auth/register
                  links:
                    signup: https://gpucloud.ca/en/auth/register
                    login: https://gpucloud.ca/en/auth/login
                    apiKeys: https://gpucloud.ca/en/console/settings#api-keys
                    billing: https://gpucloud.ca/en/console/billing
                    addCredits: https://gpucloud.ca/en/console/billing?addCredits=25
                    paymentMethod: https://gpucloud.ca/en/console/billing?addCredits=25#payment-method
                    budget: https://gpucloud.ca/en/console/billing#budget-limit
                    documentation: https://gpucloud.ca/en/developers#errors
            invalidKey:
              summary: Invalid, expired or revoked key
              value:
                error: Unauthorized
                message: 'This API key is invalid, expired, revoked or lacks the required permission. Create a new API key in the console (https://gpucloud.ca/en/console/settings#api-keys) and send it in the header Authorization: Bearer gpu_... No account yet: https://gpucloud.ca/en/auth/register'
                accountAction:
                  code: INVALID_API_KEY
                  message: 'This API key is invalid, expired, revoked or lacks the required permission. Create a new API key in the console (https://gpucloud.ca/en/console/settings#api-keys) and send it in the header Authorization: Bearer gpu_... No account yet: https://gpucloud.ca/en/auth/register'
                  action:
                    label: Create an API key
                    url: https://gpucloud.ca/en/console/settings#api-keys
                  links:
                    signup: https://gpucloud.ca/en/auth/register
                    login: https://gpucloud.ca/en/auth/login
                    apiKeys: https://gpucloud.ca/en/console/settings#api-keys
                    billing: https://gpucloud.ca/en/console/billing
                    addCredits: https://gpucloud.ca/en/console/billing?addCredits=25
                    paymentMethod: https://gpucloud.ca/en/console/billing?addCredits=25#payment-method
                    budget: https://gpucloud.ca/en/console/billing#budget-limit
                    documentation: https://gpucloud.ca/en/developers#errors
    InternalError:
      description: Internal server error
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error: Internal server error
    BadRequest:
      description: Invalid request
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error: Invalid input
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      description: 'REST API key (prefix gpu_) created in the GPU Cloud console (Settings). Pass it as `Authorization: Bearer <your-api-key>`. Keys expire after 90 days; a read-only key may only call GET. Write requests (POST, PUT, DELETE) must also send `Origin: https://gpucloud.ca`.'