openapi: 3.1.0
info:
  title: Ornn compute API
  version: 1.0.0
  description: Customer HTTP API for Ornn GPU capacity. It covers reserved capacity and nodes, VM and Bare Metal access, Kubernetes and Slurm clusters, SSH keys, storage volumes and GitHub repositories, private networks, monitoring, buying and selling capacity (Reserve, Market, Spot, and Relist), and billing. API keys are scoped to one organization and act with their creator's access there; account, team, and API-key management stay in the console. Most operations require an approved organization. See https://docs.ornn.com/api-keys to create a key and https://docs.ornn.com/guides/kubernetes-api for an end-to-end Kubernetes integration. Examples use fictional IDs and credentials. The info.version field identifies the contract revision, not a shared API path version.
  contact:
    email: support@ornn.com
servers:
  - url: https://api.ornn.com
    description: Production
security:
  - OrnnApiKey: []
tags:
  - name: Authentication
  - name: Reservations
  - name: GPU nodes
  - name: Clusters
  - name: Workers
  - name: Virtual machines
    description: Choose a VM disk, launch VM access on a reservation, and read each VM's status, launch progress, and SSH connection details.
  - name: Bare Metal
    description: Launch Bare Metal access on a reservation, read a host's readiness and SSH connection details, and reboot or hard reset it.
  - name: SSH keys
    description: Register public keys for your organization, choose which keys a VM or Bare Metal reservation uses, and queue key delivery to machines. Slurm clusters take organization key IDs at launch. Kubernetes API access does not use SSH keys.
  - name: Reserve
    description: Browse reserved-capacity listings and their terms, and post, cancel, or accept offers. Reserve listings are visible to approved organizations.
  - name: Market
    description: Browse relisted capacity and buy windows at the asking price, paid up front. Open to every member of your organization.
  - name: Spot
    description: "Hourly order books for VMs with 1, 2, 4, or 8 GPUs on one node: read books, trades, and quotes, place and cancel bids, and follow fills."
  - name: Relist
    description: Sell whole nodes from a reservation you hold at your asking price, and manage those listings.
  - name: Storage
    description: >-
      Managed volumes, their files, and GitHub repository pulls. Volumes belong to the API key's creator
      in the key's organization.
  - name: Networks
    description: >-
      Organization private networks and the WireGuard configs you use to reach private machines and
      clusters.
  - name: Monitoring
    description: >-
      Machine telemetry and console logs for capacity your organization holds. Reads are limited to your
      reservation window.
  - name: Billing
    description: >-
      Invoices, billing summary, usage cost, Ornn credits, and account statements. Saving a card and paying
      invoices stay in the console.
paths:
  /v1/auth/session:
    get:
      summary: Check API authentication
      operationId: getSession
      tags:
        - Authentication
      description: Returns the account and organizations visible to the credential. An API key sees exactly its own organization and has no staff privileges.
      responses:
        "200":
          description: Authenticated account and the API key organization.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Session"
              example:
                user_id: 55555555-5555-4555-8555-555555555555
                email: developer@example.com
                name: Example Developer
                role: user
                organizations:
                  - id: 22222222-2222-4222-8222-222222222222
                    name: Example organization
                    approved: true
                    role: consumer
                impersonating: false
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        default:
          $ref: "#/components/responses/Error"
  /commerce/reservations:
    get:
      summary: List reservations
      operationId: listReservations
      tags:
        - Reservations
      description: Lists reservations visible to your organization, including its held reservations and sales against listings it supplies. Visibility of a sale does not grant the supplier the buyer's cluster-launch rights. Follow nextCursor until absent. This endpoint does not purchase capacity.
      responses:
        "200":
          description: One page of visible reservations. This example is the last page.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ReservationPage"
              example:
                reservations:
                  - reservationId: 11111111-1111-4111-8111-111111111111
                    customerId: 22222222-2222-4222-8222-222222222222
                    status: active
                    isSpot: false
                    provisioningType: kubernetes
                    gpuSlug: h100
                    gpusPerNode: 8
                    reservedNodeCount: 1
                    reservedGpuCount: 8
                    startsAt: "2026-10-14T00:00:00Z"
                    endsAt: "2026-10-21T00:00:00Z"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        default:
          $ref: "#/components/responses/Error"
        "400":
          description: Invalid query parameter.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: invalid_limit
                  message: Choose a page size between 1 and 100.
      parameters:
        - name: ids
          in: query
          required: false
          description: >-
            Comma-separated reservation IDs, supplied once. Intersects the other filters and your
            organization access. Missing or inaccessible reservations are omitted; repeated IDs return
            one record. Pass limit=100 to retrieve a full batch in one page.
          style: form
          explode: false
          schema:
            type: array
            minItems: 1
            maxItems: 100
            items:
              type: string
              format: uuid
              not:
                const: "00000000-0000-0000-0000-000000000000"
          example:
            - 11111111-1111-4111-8111-111111111111
            - 33333333-3333-4333-8333-333333333333
        - name: status
          in: query
          required: false
          description: Filter by reservation status.
          schema:
            type: string
            enum:
              - confirmed
              - active
              - completed
              - cancelled
        - name: limit
          in: query
          required: false
          description: Page size, at most 100. Defaults to 50.
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 50
          example: 100
        - name: cursor
          in: query
          required: false
          description: Opaque nextCursor from the preceding response.
          schema:
            type: string
        - name: deployed
          in: query
          required: false
          description: Filter by whether the reservation has been deployed.
          schema:
            type: boolean
        - name: endsAtNull
          in: query
          required: false
          description: Filter by whether the reservation has no end time.
          schema:
            type: boolean
        - name: listingId
          in: query
          required: false
          description: Filter by listing ID.
          schema:
            type: string
            format: uuid
        - name: listingTermId
          in: query
          required: false
          description: Filter by listing term ID.
          schema:
            type: string
            format: uuid
        - name: customerId
          in: query
          required: false
          description: Customer filter; cannot widen your organization access.
          schema:
            type: string
            format: uuid
  /commerce/reservations/{reservationId}:
    get:
      summary: Get a reservation
      operationId: getReservation
      tags:
        - Reservations
      description: Returns one reservation visible to your organization. A reservation is not a cluster; launching a cluster is a separate operation.
      responses:
        "200":
          description: The visible reservation. Additional reservation fields are described below.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Reservation"
              example:
                reservationId: 11111111-1111-4111-8111-111111111111
                customerId: 22222222-2222-4222-8222-222222222222
                status: active
                isSpot: false
                provisioningType: kubernetes
                gpuSlug: h100
                gpusPerNode: 8
                reservedNodeCount: 1
                reservedGpuCount: 8
                startsAt: "2026-10-14T00:00:00Z"
                endsAt: "2026-10-21T00:00:00Z"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        default:
          $ref: "#/components/responses/Error"
      parameters:
        - name: reservationId
          in: path
          required: true
          description: Reservation ID.
          schema:
            type: string
            format: uuid
          example: 11111111-1111-4111-8111-111111111111
  /v1/orchestrator/nodes:
    get:
      summary: List reserved GPU nodes
      operationId: listReservationNodes
      tags:
        - GPU nodes
      description: Returns up to 5,000 nodes belonging to an owned reservation. The customer response has no cursor; limit and cursor do not paginate this reservation-scoped read. A listed node is not necessarily ready to join a cluster. Check health_readiness for heartbeat-based launch blockers; ready true with heartbeat:unknown is not proof of a reachable host. Launch rechecks eligibility, ownership, and availability. This reference describes the buyer reservation view.
      responses:
        "200":
          description: Nodes associated with the owned reservation; launch eligibility is checked separately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/NodeList"
              example:
                nodes:
                  - node_id: 33333333-3333-4333-8333-333333333333
                    reservation_id: 11111111-1111-4111-8111-111111111111
                    controller_id:
                    status: bare-metal
                    health_readiness:
                      ready: true
                      status: ready
                      reasons: []
                      advisory_reasons: []
                    gpu_type: H100
                    gpu_count: 8
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        default:
          $ref: "#/components/responses/Error"
      parameters:
        - name: reservation_id
          in: query
          required: true
          description: Owned reservation to inspect.
          schema:
            type: string
            format: uuid
          example: 11111111-1111-4111-8111-111111111111
  /v1/orchestrator/controllers:
    get:
      summary: List clusters and workers
      operationId: listClusters
      tags:
        - Clusters
      description: Returns up to 500 of your organization's newest Kubernetes and Slurm clusters, each with its workers. A cluster leaves this list when its teardown finishes. There is no cursor and no customer GET-by-ID route. Select one cluster by controller.id. Your organization runs at most one live cluster of each kind, so after a create times out, the live cluster of that kind is the one it created. Poll this resource to follow launch, worker, and teardown progress. controller.state is active once every launch worker has joined and access has been issued; it does not prove Kubernetes Node readiness or GPU capacity. Because the list is bounded, confirm that a specific cluster's teardown finished with getClusterAccess, which then returns 404 cluster_not_found.
      responses:
        "200":
          description: Clusters with their worker memberships. The example shows an active Kubernetes cluster.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ClusterList"
              example:
                controllers:
                  - controller:
                      id: 44444444-4444-4444-8444-444444444444
                      kind: kubernetes
                      state: active
                      network_mode: public
                      network_id:
                      endpoint: https://cluster.example.com:6443
                      credential_state: active
                      metadata:
                        namespace: k8s-22222222-2222-4222-8222-222222222222
                      backend_metadata:
                        kubernetes_launch:
                          reservation_id: 11111111-1111-4111-8111-111111111111
                      progress:
                        stages:
                          - key: create_plane
                            label: Create the control plane
                            status: completed
                            detail:
                          - key: prepare_hosts
                            label: Prepare the hosts
                            status: completed
                            detail:
                          - key: join_workers
                            label: Join the workers
                            status: completed
                            detail:
                          - key: apply_plane
                            label: Apply the control plane
                            status: completed
                            detail:
                          - key: issue_access
                            label: Issue access
                            status: completed
                            detail:
                      created_at: "2026-10-14T12:00:00Z"
                      updated_at: "2026-10-14T12:20:00Z"
                    nodes:
                      - node_id: 33333333-3333-4333-8333-333333333333
                        controller_id: 44444444-4444-4444-8444-444444444444
                        reservation_id: 11111111-1111-4111-8111-111111111111
                        role: worker
                        state: ready
                        gpu_count: 8
                        linux_username: tenant
                        ssh_port: 22
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        default:
          $ref: "#/components/responses/Error"
      parameters:
        - name: kind
          in: query
          required: false
          description: Filter by cluster type.
          schema:
            type: string
            enum:
              - kubernetes
              - slurm
          example: kubernetes
    post:
      summary: Create a cluster
      operationId: createCluster
      tags:
        - Clusters
      description: "Launches a Kubernetes or Slurm cluster on capacity your organization already holds; it does not purchase GPUs. List the hosts to use in node_ids. Each must be in bare-metal status, not in another cluster, and on an active reservation your organization owns; hosts can come from more than one such reservation. If node_ids and virtual_machine_ids are both omitted or empty, every bare-metal host on reservation_id is used. Before a host joins, Ornn prepares it - running processes are stopped, the tenant home directory is wiped, and any VM on that host is removed - so save its data first.\n\nYour organization can run one cluster of each kind at a time, and cannot run Kubernetes and Slurm clusters at the same time. A cluster counts until its teardown finishes. A repeat request for a reservation whose cluster is active, or provisioning with workers attached, returns that cluster with 202 and changes nothing, even if the node list differs; change membership with the worker operations. A request after a failed launch on the same reservation relaunches it under the same id. Do not create while a teardown is in progress.\n\nA 202 response means the launch was accepted, not that the cluster is ready. Poll listClusters until state is active or failed, then call getClusterAccess. This route does not read an Idempotency-Key header; after a timeout or 5xx, list clusters and look for the reservation before sending the request again. Unknown body fields are rejected. Malformed JSON, unknown fields, or a missing Content-Type return 400, 422, or 415 with a plain-text body."
      responses:
        "202":
          description: Launch accepted. node_count and gpu_count count workers attached so far and are usually 0 in this response. Poll listClusters for progress.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ClusterAccepted"
              example:
                id: 44444444-4444-4444-8444-444444444444
                kind: kubernetes
                organization_id: 22222222-2222-4222-8222-222222222222
                access_mode: kubernetes
                state: provisioning
                network_mode: public
                endpoint:
                credential_state: pending
                namespace: k8s-22222222-2222-4222-8222-222222222222
                node_count: 0
                gpu_count: 0
                progress:
                created_at: "2026-10-14T12:00:00Z"
                updated_at: "2026-10-14T12:00:00Z"
        "400":
          description: The request names an unsupported cluster kind or repeats a host.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              examples:
                unsupportedKind:
                  summary: Unsupported cluster kind
                  value:
                    detail:
                      code: unsupported_controller_kind
                      message: That cluster type is not supported. Choose Kubernetes or Slurm.
                duplicateNode:
                  summary: The same host is listed twice
                  value:
                    detail:
                      code: cluster_nodes_duplicate
                      message: Each node can only be selected once. Review the node selection and try again.
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          description: The reservation, a listed host, or a listed VM is not visible to your organization. A supplier's view of a sale does not grant launch rights.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              examples:
                reservation:
                  summary: Reservation not owned or not found
                  value:
                    detail:
                      code: reservation_not_found
                      message: Ornn could not find that reservation.
                node:
                  summary: A listed host is not owned or does not exist
                  value:
                    detail:
                      code: node_not_found
                      message: Ornn could not find that node in this cluster.
                virtualMachine:
                  summary: A listed VM is not owned, does not exist, or is offline
                  value:
                    detail:
                      code: virtual_machine_not_found
                      message: Ornn could not find that virtual machine.
        "409":
          description: A lifecycle or capacity conflict prevents launch. Read detail.code and current state before retrying. launch_node_offline names the host and its last heartbeat at the top level. cluster_access_mode_conflict adds live_mode, the kind of cluster your organization already runs.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              examples:
                otherKindRunning:
                  summary: A cluster of the other kind is still live
                  value:
                    detail:
                      code: cluster_access_mode_conflict
                      message: This reservation already has a live cluster in another access mode. Tear it down before launching a different cluster type.
                    live_mode: slurm
                otherClusterRunning:
                  summary: Your organization already runs a Kubernetes cluster on another reservation
                  value:
                    detail:
                      code: kubernetes_cluster_ambiguous_group
                      message: These nodes belong to more than one Kubernetes cluster. Choose nodes from one cluster.
                nodeOffline:
                  summary: A selected host has not sent a heartbeat recently
                  value:
                    detail:
                      code: launch_node_offline
                      message: That node is offline. Power it on and try the launch again.
                    node_id: 33333333-3333-4333-8333-333333333333
                    last_heartbeat_at: "2026-10-14T11:40:00+00:00"
                    heartbeat_max_age_seconds: 180
                nodesUnavailable:
                  summary: A selected host is not in bare-metal status
                  value:
                    detail:
                      code: cluster_nodes_not_available
                      message: One or more selected nodes are no longer available. Refresh the node list and try again.
                reservationEnded:
                  summary: The reservation ended, was cancelled, or has a pending change
                  value:
                    detail:
                      code: reservation_not_launchable
                      message: This reservation cannot launch in its current state.
                sameHost:
                  summary: A host and a VM on that host were both listed
                  value:
                    detail:
                      code: node_virtual_machine_same_host
                      message: Choose that node or its virtual machine, not both.
                slurmKeyRequired:
                  summary: Slurm launch with no active organization SSH key
                  value:
                    detail:
                      code: slurm_ssh_key_required
                      message: Add an SSH key to your account first. Slurm logins use your saved account SSH keys.
                slurmNodeCount:
                  summary: Slurm node_count does not match the hosts that resolved
                  value:
                    error: cluster_nodes_requested_unavailable
                    detail: cluster_nodes_requested_unavailable
                    requested: 2
                    available: 1
        "422":
          description: Invalid cluster configuration. The example uses an unsupported network mode.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: invalid_network_mode
                  message: Choose Public or Private VPN for the cluster network.
        default:
          $ref: "#/components/responses/Error"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateCluster"
            examples:
              kubernetes:
                summary: Kubernetes on one selected host
                value:
                  kind: kubernetes
                  reservation_id: 11111111-1111-4111-8111-111111111111
                  network_mode: public
                  node_ids:
                    - 33333333-3333-4333-8333-333333333333
              slurm:
                summary: Slurm on two selected hosts with one SSH key
                value:
                  kind: slurm
                  reservation_id: 11111111-1111-4111-8111-111111111111
                  network_mode: public
                  node_ids:
                    - 33333333-3333-4333-8333-333333333333
                    - 77777777-7777-4777-8777-777777777777
                  ssh_key_ids:
                    - 88888888-8888-4888-8888-888888888888
  /v1/orchestrator/controllers/{controller_id}/access:
    get:
      summary: Get cluster access
      operationId: getClusterAccess
      tags:
        - Clusters
      description: "Returns the issued access for an owned cluster. A Kubernetes cluster returns a kubeconfig, its API server endpoint, and the namespace Ornn configured for your workloads. A Slurm cluster returns its SSH login details. While a launch is still running, 404 kubernetes_credentials_not_ready or slurm_credentials_not_ready means access has not been issued yet; keep polling listClusters. 404 cluster_not_found means the cluster does not exist or is not visible to your organization, including after its teardown finishes. Access stays readable while a teardown is in progress.\n\nThe kubeconfig token is issued for seven days and grants cluster-admin. Reading this endpoint again returns the same stored kubeconfig; it does not renew the token. The Ornn API key is not a Kubernetes credential."
      responses:
        "200":
          description: Issued access. The examples use fictional credentials.
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: "#/components/schemas/KubernetesAccess"
                  - $ref: "#/components/schemas/SlurmAccess"
                discriminator:
                  propertyName: kind
                  mapping:
                    kubernetes: "#/components/schemas/KubernetesAccess"
                    slurm: "#/components/schemas/SlurmAccess"
              examples:
                kubernetes:
                  summary: Kubernetes cluster
                  value:
                    kind: kubernetes
                    kubeconfig: |
                      apiVersion: v1
                      kind: Config
                      clusters:
                      - name: k8s-22222222-2222-4222-8222-222222222222
                        cluster:
                          server: https://cluster.example.com:6443
                          certificate-authority-data: <base64-cluster-ca>
                      contexts:
                      - name: k8s-22222222-2222-4222-8222-222222222222
                        context:
                          cluster: k8s-22222222-2222-4222-8222-222222222222
                          namespace: k8s-22222222-2222-4222-8222-222222222222
                          user: ornn-tenant
                      current-context: k8s-22222222-2222-4222-8222-222222222222
                      users:
                      - name: ornn-tenant
                        user:
                          token: <cluster-credential>
                    endpoint: https://cluster.example.com:6443
                    namespace: k8s-22222222-2222-4222-8222-222222222222
                slurm:
                  summary: Slurm cluster
                  value:
                    kind: slurm
                    host: 203.0.113.10
                    node_id: 33333333-3333-4333-8333-333333333333
                    port: 30022
                    ssh_host_key: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIExampleHostKeyOnly
                    login_user: tenant
                    ssh_command: ssh -p 30022 tenant@203.0.113.10
                    login_host: 203.0.113.10
                    login_port: 30022
                    authorized_key_fingerprints:
                      - SHA256:ExampleFingerprintOnly
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          description: Access has not been issued yet, or the cluster is not visible. Only the two credentials_not_ready codes mean that access is still being issued.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              examples:
                kubernetesNotReady:
                  summary: Kubernetes access not issued yet
                  value:
                    detail:
                      code: kubernetes_credentials_not_ready
                      message: Kubernetes credentials are not ready yet. The cluster is still issuing access.
                slurmNotReady:
                  summary: Slurm login not issued yet
                  value:
                    detail:
                      code: slurm_credentials_not_ready
                      message: SSH login access is not ready yet. The Slurm scheduler is still starting.
                clusterNotFound:
                  summary: Cluster absent, not visible, or torn down
                  value:
                    detail:
                      code: cluster_not_found
                      message: Ornn could not find that cluster. It may already be torn down.
        default:
          $ref: "#/components/responses/Error"
      parameters:
        - name: controller_id
          in: path
          required: true
          description: Cluster id returned by create or list; not a reservation ID.
          schema:
            type: string
            format: uuid
          example: 44444444-4444-4444-8444-444444444444
  /v1/orchestrator/controllers/{controller_id}/nodes/{node_id}:
    post:
      summary: Add a physical worker
      operationId: addNode
      tags:
        - Workers
      description: "Adds one host to an owned cluster that is not failed or being torn down. The host must be in bare-metal status, not in another cluster, and on an active reservation your organization owns; it can come from a different reservation than the one the cluster launched on. A VM and its host cannot both be cluster workers.\n\nA new host normally returns state cleaning. Ornn first prepares the host - running processes are stopped, the tenant home directory is wiped, and any VM on that host is removed - and the host then appears in the cluster's nodes in listClusters with state joining, then ready. Repeating the request is safe. It returns cleaning while preparation is pending, or the current membership state once the host is a member.\n\nFor Kubernetes, adding a worker does not raise the GPU quota of the namespace Ornn configured, which is sized when the cluster first becomes active. A host from another reservation is labeled and tainted with its own reservation, while pods in that namespace are pinned to the reservation the cluster launched on. Verify Node readiness, allocatable GPUs, and the namespace quota through the Kubernetes API before scheduling more work."
      responses:
        "202":
          description: Addition accepted, or the host is already a member. Preparation and joining continue asynchronously.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WorkerChange"
              example:
                controller_id: 44444444-4444-4444-8444-444444444444
                node_id: 33333333-3333-4333-8333-333333333333
                state: cleaning
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          description: The cluster is absent, failed, or being torn down, or the host is not owned.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              examples:
                cluster:
                  summary: Cluster absent, failed, or being torn down
                  value:
                    detail:
                      code: controller_not_found
                      message: Ornn could not find that cluster. It may already be torn down.
                node:
                  summary: Host not owned or not found
                  value:
                    detail:
                      code: node_not_found
                      message: Ornn could not find that node in this cluster.
        "409":
          description: The host cannot join. A 409 with an empty JSON object means the host already belongs to another cluster or runs another workload.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              examples:
                notBareMetal:
                  summary: The host or an existing member is not in bare-metal status
                  value:
                    detail:
                      code: cluster_nodes_not_available
                      message: One or more selected nodes are no longer available. Refresh the node list and try again.
                reservationEnded:
                  summary: The host's reservation ended, was cancelled, or has a pending change
                  value:
                    detail:
                      code: reservation_not_launchable
                      message: This reservation cannot launch in its current state.
                noReservation:
                  summary: The host is not on a reservation
                  value:
                    detail:
                      code: node_reservation_missing
                      message: This node is not part of a reservation, so it cannot join a cluster.
                inUse:
                  summary: The host belongs to another cluster
                  value: {}
        default:
          $ref: "#/components/responses/Error"
      parameters:
        - name: controller_id
          in: path
          required: true
          description: Cluster id returned by create or list; not a reservation ID.
          schema:
            type: string
            format: uuid
          example: 44444444-4444-4444-8444-444444444444
        - name: node_id
          in: path
          required: true
          description: An existing owned physical worker ID.
          schema:
            type: string
            format: uuid
          example: 33333333-3333-4333-8333-333333333333
    delete:
      summary: Remove a physical worker
      operationId: removeNode
      tags:
        - Workers
      description: Starts removing a host from an owned cluster. Workloads on that host stop, and its local data can be lost. The 200 response with state draining means cleanup was sent, not finished. Poll listClusters - the host leaves the cluster's nodes after it confirms cleanup. A member left in state cleanup_failed reported that cleanup did not finish; contact Ornn before reusing it. Repeating the request while the host is draining sends cleanup again; once the host has left, it returns 404 node_not_found. Removing a worker does not cancel its reservation or stop its billing.
      responses:
        "200":
          description: Removal started; draining and cleanup are asynchronous.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WorkerChange"
              example:
                controller_id: 44444444-4444-4444-8444-444444444444
                node_id: 33333333-3333-4333-8333-333333333333
                state: draining
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          description: The cluster is not visible, or the host is not one of its members.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              examples:
                cluster:
                  summary: Cluster absent or not visible
                  value:
                    detail:
                      code: controller_not_found
                      message: Ornn could not find that cluster. It may already be torn down.
                node:
                  summary: Host not owned, or not a member
                  value:
                    detail:
                      code: node_not_found
                      message: Ornn could not find that node in this cluster.
        default:
          $ref: "#/components/responses/Error"
      parameters:
        - name: controller_id
          in: path
          required: true
          description: Cluster id returned by create or list; not a reservation ID.
          schema:
            type: string
            format: uuid
          example: 44444444-4444-4444-8444-444444444444
        - name: node_id
          in: path
          required: true
          description: An existing owned physical worker ID.
          schema:
            type: string
            format: uuid
          example: 33333333-3333-4333-8333-333333333333
  /v1/orchestrator/controllers/{controller_id}/virtual-machines/{virtual_machine_id}:
    post:
      summary: Add a VM worker
      operationId: addVirtualMachine
      tags:
        - Workers
      description: Adds an existing VM to an owned cluster that is not failed or being torn down. The VM must be on a reservation your organization owns, must not be deleted, destroyed, or offline, and must not already be in another cluster. A VM and its host cannot both be cluster workers. The 202 response accepts the operation with state joining; repeating it for a VM that is already a member returns its current state. Verify worker readiness through the Kubernetes API before scheduling more work.
      responses:
        "202":
          description: VM addition accepted, or the VM is already a member. Joining continues asynchronously.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WorkerChange"
              example:
                controller_id: 44444444-4444-4444-8444-444444444444
                virtual_machine_id: 66666666-6666-4666-8666-666666666666
                state: joining
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          description: The cluster is absent, failed, or being torn down, or the VM is not visible.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              examples:
                cluster:
                  summary: Cluster absent, failed, or being torn down
                  value:
                    detail:
                      code: controller_not_found
                      message: Ornn could not find that cluster. It may already be torn down.
                virtualMachine:
                  summary: VM not owned, not found, or offline
                  value:
                    detail:
                      code: virtual_machine_not_found
                      message: Ornn could not find that virtual machine.
        "409":
          description: The VM is already a worker in another cluster.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: virtual_machine_already_attached
                  message: This virtual machine is already in another cluster.
        default:
          $ref: "#/components/responses/Error"
      parameters:
        - name: controller_id
          in: path
          required: true
          description: Cluster id returned by create or list; not a reservation ID.
          schema:
            type: string
            format: uuid
          example: 44444444-4444-4444-8444-444444444444
        - name: virtual_machine_id
          in: path
          required: true
          description: An existing owned VM worker ID.
          schema:
            type: string
            format: uuid
          example: 66666666-6666-4666-8666-666666666666
    delete:
      summary: Remove a VM worker
      operationId: removeVirtualMachine
      tags:
        - Workers
      description: Starts removing a VM worker from an owned cluster. Workloads on it stop, and its local data can be lost. The 200 response with state draining means cleanup was sent, not finished. The cluster list reports host node_id values, not virtual_machine_id, so it cannot confirm removal of this VM by its ID. Repeating the request after the VM has left the cluster returns 404 virtual_machine_not_in_cluster. Confirm cleanup with Ornn before reusing the VM. Removing a worker does not cancel its reservation.
      responses:
        "200":
          description: VM removal started; draining and cleanup are asynchronous.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WorkerChange"
              example:
                controller_id: 44444444-4444-4444-8444-444444444444
                virtual_machine_id: 66666666-6666-4666-8666-666666666666
                state: draining
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          description: The cluster or VM is not visible, or the VM is not a member of this cluster.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              examples:
                cluster:
                  summary: Cluster absent or not visible
                  value:
                    detail:
                      code: controller_not_found
                      message: Ornn could not find that cluster. It may already be torn down.
                virtualMachine:
                  summary: VM not owned, not found, or offline
                  value:
                    detail:
                      code: virtual_machine_not_found
                      message: Ornn could not find that virtual machine.
                notMember:
                  summary: VM is not a member of this cluster
                  value:
                    detail:
                      code: virtual_machine_not_in_cluster
                      message: That virtual machine is not part of this cluster.
        default:
          $ref: "#/components/responses/Error"
      parameters:
        - name: controller_id
          in: path
          required: true
          description: Cluster id returned by create or list; not a reservation ID.
          schema:
            type: string
            format: uuid
          example: 44444444-4444-4444-8444-444444444444
        - name: virtual_machine_id
          in: path
          required: true
          description: An existing owned VM worker ID.
          schema:
            type: string
            format: uuid
          example: 66666666-6666-4666-8666-666666666666
  /v1/orchestrator/controllers/{controller_id}/teardown:
    post:
      summary: Tear down a cluster
      operationId: teardownCluster
      tags:
        - Clusters
      description: "Stops a launch in progress or tears down a cluster. Ornn deletes the cluster's workloads, revokes its access, and sends cleanup to every worker. The cluster stays in state deleting, with teardown stages in progress, while Ornn waits up to five minutes for workers to confirm cleanup; then its record is removed. Workers that never confirm are released anyway and cleaned when they reconnect. Reservation billing continues, and separately provisioned filesystems are not deleted.\n\nRepeating the request while the cluster is deleting returns only its state and does not restart cleanup. Teardown is complete when the cluster is absent from listClusters and getClusterAccess returns 404 cluster_not_found; this operation then returns 404 with an empty body. If the state becomes failed instead, send the teardown again, and contact Ornn if it fails twice. Wait for teardown to finish before creating another cluster."
      responses:
        "202":
          description: Teardown accepted, or already in progress. Poll for completion. Reservation billing continues.
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: "#/components/schemas/ClusterAccepted"
                  - type: object
                    required:
                      - state
                    properties:
                      state:
                        type: string
                    additionalProperties: false
              examples:
                accepted:
                  summary: First teardown request
                  value:
                    id: 44444444-4444-4444-8444-444444444444
                    kind: kubernetes
                    organization_id: 22222222-2222-4222-8222-222222222222
                    access_mode: kubernetes
                    state: deleting
                    network_mode: public
                    endpoint:
                    credential_state: pending
                    namespace: k8s-22222222-2222-4222-8222-222222222222
                    node_count: 1
                    gpu_count: 8
                    progress:
                      stages:
                        - key: workers
                          label: Release the workers
                          status: running
                          detail: 0 of 1 confirmed
                        - key: namespace
                          label: Delete the cluster namespace
                          status: running
                          detail:
                    created_at: "2026-10-14T12:00:00Z"
                    updated_at: "2026-10-14T13:00:00Z"
                repeated:
                  summary: Repeat request while the teardown runs
                  value:
                    state: deleting
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          description: The cluster does not exist or is not visible to your organization, including after its teardown finished. This response has no body.
        default:
          $ref: "#/components/responses/Error"
      parameters:
        - name: controller_id
          in: path
          required: true
          description: Cluster id returned by create or list; not a reservation ID.
          schema:
            type: string
            format: uuid
          example: 44444444-4444-4444-8444-444444444444
  /commerce/reservations/{reservationId}/disk:
    put:
      summary: Choose a VM disk for a reservation
      operationId: accessSetReservationDisk
      tags:
        - Virtual machines
      description: Saves the disk Ornn boots when it creates this reservation's VMs. Saving a disk does not launch anything. You can change the choice until the reservation is activated; after activation the disk is locked. Send null to clear the choice, in which case activation uses your organization's newest disk, or an Ornn disk if your organization has none. Ornn checks that your organization can use the disk when it creates the VM. Bare Metal, Kubernetes, and Slurm reservations do not use a VM disk.
      parameters:
        - name: reservationId
          in: path
          required: true
          description: Reservation ID.
          schema:
            type: string
            format: uuid
          example: 11111111-1111-4111-8111-111111111111
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/AccessReservationDiskRequest"
            example:
              vmDiskId: 77777777-7777-4777-8777-777777777777
      responses:
        "200":
          description: The saved choice. The reservation now carries the disk in vmDiskId.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Reservation"
              example:
                reservationId: 11111111-1111-4111-8111-111111111111
                customerId: 22222222-2222-4222-8222-222222222222
                status: confirmed
                isSpot: false
                provisioningType: virtual-machine
                gpuSlug: h100
                gpusPerNode: 8
                reservedNodeCount: 1
                reservedGpuCount: 8
                startsAt: "2026-10-15T00:00:00Z"
                endsAt: "2026-10-22T00:00:00Z"
                vmDiskId: 77777777-7777-4777-8777-777777777777
                deployedAt:
                pendingOperation:
        "400":
          description: The body is not valid JSON or contains an unknown field.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "409":
          description: The reservation is already activated or has ended, or another reservation operation is pending.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: reservation_disk_locked
                  message: Choose the disk before activating this reservation.
        "422":
          description: The reservation does not boot a VM disk, or the disk ID is not valid.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: disk_not_supported
                  message: Only virtual machine reservations boot a disk.
        default:
          $ref: "#/components/responses/Error"
  /commerce/reservations/{reservationId}/activate:
    post:
      summary: Activate a VM or Bare Metal reservation
      operationId: accessActivateReservation
      tags:
        - Virtual machines
        - Bare Metal
      description: Requests machines for a VM or Bare Metal reservation during its reserved window. Ornn also activates reservations automatically when their window starts, so you only need this call to start sooner within the window or to retry a pending activation. A VM reservation gets one VM per reserved node, booted from the saved disk; a Bare Metal reservation is assigned its hosts. Activation does not install your SSH keys; launch access afterward. A 200 response means machines were requested, not that they are reachable. Repeating the call on an activated reservation returns it unchanged. If you receive 409 runtime_pending, repeat the same request. Kubernetes and Slurm reservations use the cluster routes instead. Requires an approved organization.
      parameters:
        - name: reservationId
          in: path
          required: true
          description: Reservation ID.
          schema:
            type: string
            format: uuid
          example: 11111111-1111-4111-8111-111111111111
      responses:
        "200":
          description: Activation requested. deployedAt is set when machines were requested.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Reservation"
              example:
                reservationId: 11111111-1111-4111-8111-111111111111
                customerId: 22222222-2222-4222-8222-222222222222
                status: active
                isSpot: false
                provisioningType: virtual-machine
                gpuSlug: h100
                gpusPerNode: 8
                reservedNodeCount: 1
                reservedGpuCount: 8
                startsAt: "2026-10-15T00:00:00Z"
                endsAt: "2026-10-22T00:00:00Z"
                vmDiskId: 77777777-7777-4777-8777-777777777777
                deployedAt: "2026-10-15T00:02:11Z"
                pendingOperation:
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "409":
          description: The reservation is outside its window or has ended, another operation is pending, or machine changes have not finished. Retry the same request for runtime_pending.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: outside_reservation_window
                  message: Activate this reservation during its reserved time window.
        "422":
          description: The reservation is a Kubernetes or Slurm reservation, or no VM disk is available.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: controller_required
                  message: Use the cluster lifecycle for Kubernetes or Slurm reservations.
        default:
          $ref: "#/components/responses/Error"
  /commerce/reservations/{reservationId}/stop:
    post:
      summary: Stop a reservation and release its machines
      operationId: accessStopReservation
      tags:
        - Virtual machines
        - Bare Metal
      description: Ends the reservation now and releases its machines. Ornn revokes SSH access, deletes every VM in the reservation together with its local disk, and releases its Bare Metal hosts. This cannot be undone, and a stopped reservation cannot be activated again. Export local data first; separate storage volumes keep their own lifecycle. For a Spot reservation this is the Stop VM action. A relisted reservation cannot stop while its relisted windows are live. A 202 response means the stop is accepted and Ornn is still releasing the machines; read the reservation until `endsAt` is set.
      parameters:
        - name: reservationId
          in: path
          required: true
          description: Reservation ID.
          schema:
            type: string
            format: uuid
          example: 11111111-1111-4111-8111-111111111111
      responses:
        "200":
          description: The reservation has ended and its machines are released.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Reservation"
              example:
                reservationId: 11111111-1111-4111-8111-111111111111
                customerId: 22222222-2222-4222-8222-222222222222
                status: completed
                isSpot: false
                provisioningType: virtual-machine
                gpuSlug: h100
                gpusPerNode: 8
                reservedNodeCount: 1
                reservedGpuCount: 8
                startsAt: "2026-10-15T00:00:00Z"
                endsAt: "2026-10-18T09:30:00Z"
                endReason: stop
                deployedAt: "2026-10-15T00:02:11Z"
                pendingOperation:
        "202":
          description: The stop is accepted and Ornn is still releasing the machines. `pendingOperation` is `stop` until the reservation ends.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Reservation"
              example:
                reservationId: 11111111-1111-4111-8111-111111111111
                customerId: 22222222-2222-4222-8222-222222222222
                status: active
                isSpot: false
                provisioningType: virtual-machine
                gpuSlug: h100
                gpusPerNode: 8
                reservedNodeCount: 1
                reservedGpuCount: 8
                startsAt: "2026-10-15T00:00:00Z"
                endsAt:
                endReason:
                deployedAt: "2026-10-15T00:02:11Z"
                pendingOperation: stop
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "409":
          description: Another operation is pending or relisted windows are still live.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: reservation_relisted
                  message: End the child reservations before changing their parent reservation.
        default:
          $ref: "#/components/responses/Error"
  /v1/orchestrator/nodes/reservations/{reservation_id}/launch:
    post:
      summary: Launch VM or Bare Metal access
      operationId: accessLaunchReservation
      tags:
        - Virtual machines
        - Bare Metal
      description: >-
        Starts SSH access on an owned VM or Bare Metal reservation. If the reservation already has
        machines (for example after activation), launch sends the selected SSH keys to them and
        returns 202 with the current nodes. Otherwise launch selects nodes for the reservation,
        starts provisioning, and returns 201. Either response is acceptance, not readiness: poll the
        reservation's VMs or the node until SSH is ready. Repeating launch after machines exist does
        not reprovision them; it sends the keys again. Without ssh_key_ids, launch uses the keys
        already selected for the reservation, or every active SSH key in your organization. There is
        no VM size option: each VM receives its reserved GPUs and a matching share of the host's
        vCPUs, memory, and local disk, with at most 2 TiB of RAM. Launch does not attach storage
        volumes. Use Kubernetes or Slurm cluster routes for those access modes.
      parameters:
        - name: reservation_id
          in: path
          required: true
          description: An owned reservation ID (reservationId in reservation responses).
          schema:
            type: string
            format: uuid
          example: 11111111-1111-4111-8111-111111111111
      requestBody:
        required: true
        description: Send an empty object to use the reservation's defaults.
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/AccessLaunchRequest"
            examples:
              virtualMachine:
                summary: VM with one saved SSH key
                value:
                  access_mode: virtual-machine
                  network_mode: public
                  ssh_key_ids:
                    - 88888888-8888-4888-8888-888888888888
                  request_id: launch-2026-10-15-a
              bareMetal:
                summary: Bare Metal on a private network
                value:
                  access_mode: bare-metal
                  network_mode: private
                  network_id: aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa
                  ssh_key_ids:
                    - 88888888-8888-4888-8888-888888888888
      responses:
        "201":
          description: Provisioning started on the returned nodes.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AccessNodeList"
              example:
                nodes:
                  - node_id: 33333333-3333-4333-8333-333333333333
                    reservation_id: 11111111-1111-4111-8111-111111111111
                    controller_id:
                    status: bare-metal
                    hostname: gpu-host-01
                    ip: 203.0.113.10
                    port: 22
                    username: tenant
                    ssh_endpoint: tenant@203.0.113.10
                    gpu_type: H100
                    gpu_count: 8
                    authorized_key_fingerprints: []
                    authorized_key_metadata: []
                    keys_pushed_at:
                    reboot:
                    health_readiness:
                      ready: true
                      status: ready
                      reasons: []
                      advisory_reasons: []
        "202":
          description: The reservation already has machines. SSH keys were queued for delivery; the body lists the current nodes.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AccessNodeList"
              example:
                nodes:
                  - node_id: 33333333-3333-4333-8333-333333333333
                    reservation_id: 11111111-1111-4111-8111-111111111111
                    controller_id:
                    status: bare-metal
                    hostname: gpu-host-01
                    ip: 203.0.113.10
                    port: 22
                    username: tenant
                    ssh_endpoint: tenant@203.0.113.10
                    gpu_type: H100
                    gpu_count: 8
                    authorized_key_fingerprints:
                      - SHA256:exampleFingerprint0000000000000000000000000
                    authorized_key_metadata:
                      - fingerprint: SHA256:exampleFingerprint0000000000000000000000000
                        label:
                        status: installed
                        linux_username: tenant
                        pushed_at: "2026-10-15T00:06:40Z"
                        failed_at:
                        failure_reason:
                        request_id:
                    keys_pushed_at: "2026-10-15T00:06:40Z"
                    reboot:
                    health_readiness:
                      ready: true
                      status: ready
                      reasons: []
                      advisory_reasons: []
        "400":
          description: Invalid launch input, such as a VM with no disk available, an unsupported access mode, network_id without private networking, or a repeated SSH key ID. Some of these errors return detail as a plain code string.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              examples:
                diskRequired:
                  summary: No disk chosen for a VM
                  value:
                    detail: disk_required
                networkIdNotAllowed:
                  summary: network_id sent with public networking
                  value:
                    detail:
                      code: network_id_not_allowed
                      message: A named network can only be used with Private VPN. Switch to Private VPN, or leave the network as Shared.
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          description: The reservation is absent or not owned by your organization.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: reservation_not_found
                  message: Ornn could not find that reservation.
        "409":
          description: The reservation cannot launch in its current state, no active SSH key is available, not enough nodes are free, or a node still has a previous environment to clean up.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: ssh_key_required
                  message: Add an SSH key to your account before you launch. Machines are reached over SSH.
        "422":
          description: A disk was sent for Bare Metal. This error returns detail as a plain string.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail: bare_metal_disk_not_allowed
        default:
          $ref: "#/components/responses/Error"
  /v1/orchestrator/nodes/reservations/{reservation_id}/switch-access:
    post:
      summary: Switch access mode or network
      operationId: accessSwitchReservationAccess
      tags:
        - Virtual machines
        - Bare Metal
      description: >-
        Re-provisions access on the reservation's nodes with a new access mode, network mode, or both.
        Omitted fields keep the reservation's defaults. Switching between VM and Bare Metal tears down
        the existing environment and destroys data stored on the machine; copy it off first. Changing
        only the network interrupts SSH until access is ready again. A 202 response is acceptance, not
        readiness. Poll the VMs or nodes as after a launch. SSH key selection works as for launch.
      parameters:
        - name: reservation_id
          in: path
          required: true
          description: An owned reservation ID (reservationId in reservation responses).
          schema:
            type: string
            format: uuid
          example: 11111111-1111-4111-8111-111111111111
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/AccessLaunchRequest"
            example:
              access_mode: bare-metal
              network_mode: private
              ssh_key_ids:
                - 88888888-8888-4888-8888-888888888888
              request_id: switch-2026-10-16-a
      responses:
        "202":
          description: Switch accepted. The body lists the reservation's nodes.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AccessNodeList"
              example:
                nodes:
                  - node_id: 33333333-3333-4333-8333-333333333333
                    reservation_id: 11111111-1111-4111-8111-111111111111
                    controller_id:
                    status: bare-metal
                    hostname: gpu-host-01
                    ip: 203.0.113.10
                    port: 22
                    username: tenant
                    ssh_endpoint: tenant@203.0.113.10
                    gpu_type: H100
                    gpu_count: 8
                    authorized_key_fingerprints: []
                    authorized_key_metadata: []
                    keys_pushed_at:
                    reboot:
                    health_readiness:
                      ready: true
                      status: ready
                      reasons: []
                      advisory_reasons: []
        "400":
          description: Invalid switch input. Some of these errors return detail as a plain code string.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail: unsupported_access_mode
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          description: The reservation is absent or not owned by your organization.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: reservation_not_found
                  message: Ornn could not find that reservation.
        "409":
          description: The reservation cannot change in its current state, no SSH key is available, or nodes are not free.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: insufficient_available_nodes
                  message: Not enough nodes in this reservation are free right now. Lower the machine count or wait for nodes to come back.
        default:
          $ref: "#/components/responses/Error"
  /v1/orchestrator/reservations/{reservation_id}/virtual-machines:
    get:
      summary: List a reservation's VMs
      operationId: accessListReservationVirtualMachines
      tags:
        - Virtual machines
      description: >-
        Returns the reservation's VMs, oldest first, with their status, latest operation, grouped
        launch progress, and SSH details. Deleted VMs are omitted. ip and port appear while a VM is
        provisioning or running; tenant_username and ready_at appear only when it is running. Connect
        when status is running, using the returned ip, port, and tenant_username. Poll every five
        seconds with a deadline while any VM is provisioning or deleting. A failed VM carries failure
        with a code and message. You can read this list after the reservation ends.
      parameters:
        - name: reservation_id
          in: path
          required: true
          description: An owned reservation ID (reservationId in reservation responses).
          schema:
            type: string
            format: uuid
          example: 11111111-1111-4111-8111-111111111111
      responses:
        "200":
          description: The reservation's VMs. The example shows one running VM.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AccessVirtualMachineList"
              example:
                virtual_machines:
                  - id: 66666666-6666-4666-8666-666666666666
                    node_id: 33333333-3333-4333-8333-333333333333
                    reservation_id: 11111111-1111-4111-8111-111111111111
                    status: running
                    operation: provision
                    operation_id: 99999999-9999-4999-8999-999999999999
                    operation_status: succeeded
                    operation_started_at: "2026-10-15T00:02:15Z"
                    operation_completed_at: "2026-10-15T00:09:48Z"
                    stages:
                      - key: prepare_host
                        label: Preparing host
                        status: completed
                        started_at: "2026-10-15T00:02:15Z"
                        completed_at: "2026-10-15T00:02:40Z"
                      - key: start_vm
                        label: Starting virtual machine
                        status: completed
                        started_at: "2026-10-15T00:04:05Z"
                        completed_at: "2026-10-15T00:05:30Z"
                      - key: configure_access
                        label: Configuring access
                        status: completed
                        started_at: "2026-10-15T00:09:20Z"
                        completed_at: "2026-10-15T00:09:48Z"
                    failure:
                    ready_at: "2026-10-15T00:09:48Z"
                    hostname: ornn-vm-11111111-0
                    ip: 203.0.113.10
                    tenant_username: tenant
                    port: 2222
                    gpu_count: 8
                    disk_id: 77777777-7777-4777-8777-777777777777
                    created_at: "2026-10-15T00:02:11Z"
                    destroy_at:
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          description: The reservation is absent or not owned by your organization.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: reservation_not_found
                  message: Ornn could not find that reservation.
        default:
          $ref: "#/components/responses/Error"
  /v1/orchestrator/nodes/{node_id}/virtual-machines/{vm_id}/reimage:
    post:
      summary: Reimage a VM
      operationId: accessReimageVirtualMachine
      tags:
        - Virtual machines
      description: >-
        Rebuilds one VM from a disk you choose, on the same node and reservation. Everything on the
        VM's current disk is erased. The VM keeps its id, hostname, GPUs, and size. Any member of the
        reservation's organization can reimage. The VM must be running, or its last start must have
        failed. A VM on a private network stays private and keeps its private address. The new disk
        boots with the reservation's selected SSH keys, or every active key on
        your account when none is selected. A 202 response is acceptance, not readiness. The VM
        returns to provisioning; poll the reservation's VM list until status is running, then
        reconnect with the returned ip and port. If the reimage fails, the reservation stays active
        and you can reimage again.
      parameters:
        - name: node_id
          in: path
          required: true
          description: The VM's node_id from the reservation's VM list.
          schema:
            type: string
            format: uuid
          example: 33333333-3333-4333-8333-333333333333
        - name: vm_id
          in: path
          required: true
          description: The VM's id from the reservation's VM list.
          schema:
            type: string
            format: uuid
          example: 66666666-6666-4666-8666-666666666666
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - disk_id
              properties:
                disk_id:
                  type: string
                  format: uuid
                  description: >-
                    A disk from GET /v1/orchestrator/disks. Use one of your organization's disks or a
                    disk Ornn provides.
            example:
              disk_id: 77777777-7777-4777-8777-777777777777
      responses:
        "202":
          description: Reimage accepted. The body is the VM, now provisioning from the new disk.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AccessVirtualMachine"
              example:
                id: 66666666-6666-4666-8666-666666666666
                node_id: 33333333-3333-4333-8333-333333333333
                reservation_id: 11111111-1111-4111-8111-111111111111
                status: provisioning
                operation: provision
                operation_id: aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa
                operation_status: queued
                operation_started_at: "2026-10-20T14:00:00Z"
                operation_completed_at:
                stages: []
                failure:
                ready_at:
                hostname: ornn-vm-11111111-0
                ip: 203.0.113.10
                tenant_username:
                port: 2222
                gpu_count: 8
                disk_id: 77777777-7777-4777-8777-777777777777
                created_at: "2026-10-15T00:02:11Z"
                destroy_at:
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          description: The disk belongs to another organization.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: disk_admission_denied
                  message: That disk belongs to another organization. Choose one of your disks or an Ornn disk.
        "404":
          description: The VM, its node, or its reservation is absent or not owned by your organization.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: virtual_machine_not_found
                  message: Ornn could not find that virtual machine.
        "409":
          description: >-
            The VM is not running or is being removed, it is a Kubernetes or Slurm worker, its node
            is offline, its private network is not ready, or no SSH key is available.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: virtual_machine_not_reimageable
                  message: This VM cannot be reimaged right now. Reimage a running VM, or one whose last start failed, once any stop or cleanup has finished.
        default:
          $ref: "#/components/responses/Error"
  /v1/orchestrator/nodes/{node_id}/virtual-machines/{vm_id}/reboot:
    post:
      summary: Reboot a VM
      operationId: accessRebootVirtualMachine
      tags:
        - Virtual machines
      description: >-
        Restarts one running VM in place. Its disk, GPUs, SSH port, and network are kept, and other
        VMs on the node are not affected. mode reboot (the default) asks the VM's operating system to
        restart and resets the VM if it has not come back within about 90 seconds. mode reset
        power-cycles the VM at once; anything not yet written to its disk is lost. Any member of the
        reservation's organization can reboot. A 202 response is acceptance: the VM's operation
        becomes reboot. Poll the reservation's VM list until operation_status is succeeded or
        failed. A failed reboot leaves the VM running with failure set; you can reboot again.
      parameters:
        - name: node_id
          in: path
          required: true
          description: The VM's node_id from the reservation's VM list.
          schema:
            type: string
            format: uuid
          example: 33333333-3333-4333-8333-333333333333
        - name: vm_id
          in: path
          required: true
          description: The VM's id from the reservation's VM list.
          schema:
            type: string
            format: uuid
          example: 66666666-6666-4666-8666-666666666666
      requestBody:
        required: false
        description: Omit the body for a plain reboot.
        content:
          application/json:
            schema:
              type: object
              properties:
                mode:
                  type: string
                  enum:
                    - reboot
                    - reset
                  default: reboot
            example:
              mode: reboot
      responses:
        "202":
          description: Reboot accepted. The body is the VM with operation reboot.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AccessVirtualMachine"
              example:
                id: 66666666-6666-4666-8666-666666666666
                node_id: 33333333-3333-4333-8333-333333333333
                reservation_id: 11111111-1111-4111-8111-111111111111
                status: running
                operation: reboot
                operation_id: bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb
                operation_status: queued
                operation_started_at: "2026-10-20T15:00:00Z"
                operation_completed_at:
                stages: []
                failure:
                ready_at: "2026-10-15T00:09:48Z"
                hostname: ornn-vm-11111111-0
                ip: 203.0.113.10
                tenant_username: tenant
                port: 2222
                gpu_count: 8
                disk_id: 77777777-7777-4777-8777-777777777777
                created_at: "2026-10-15T00:02:11Z"
                destroy_at:
        "400":
          description: mode is not reboot or reset.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: invalid_virtual_machine_reboot_mode
                  message: Choose reboot or reset.
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          description: The VM, its node, or its reservation is absent or not owned by your organization.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: virtual_machine_not_found
                  message: Ornn could not find that virtual machine.
        "409":
          description: >-
            The VM is not running, a reboot, launch, or reimage is already in progress, it is a
            Kubernetes or Slurm worker, its node is offline, or the node needs a management update.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: virtual_machine_busy
                  message: This VM is already changing. Try again when its current reboot, launch, or reimage finishes.
        default:
          $ref: "#/components/responses/Error"
  /v1/orchestrator/nodes/{node_id}:
    get:
      summary: Get a reserved node
      operationId: accessGetNode
      tags:
        - Bare Metal
        - Virtual machines
      description: >-
        Returns one host node of an owned reservation with its SSH details, SSH key delivery,
        heartbeat readiness, and latest reboot. For Bare Metal, connect with ssh_endpoint and port
        once health_readiness.ready is true and your key's fingerprint is in
        authorized_key_fingerprints. ready true with heartbeat:unknown in advisory_reasons is not
        proof that the host is reachable; confirm with SSH. For a VM, this is the host; use the
        reservation's VM list for the VM's own status and SSH port. Pass a node_id, not a VM id. Nodes in a Kubernetes or Slurm
        cluster return 404. Reading a node of an ended reservation returns 409.
      parameters:
        - name: node_id
          in: path
          required: true
          description: A host node_id from the reservation's node list. Not a VM id.
          schema:
            type: string
            format: uuid
          example: 33333333-3333-4333-8333-333333333333
      responses:
        "200":
          description: The node. The example shows a Bare Metal host with one installed key.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AccessNode"
              example:
                node_id: 33333333-3333-4333-8333-333333333333
                reservation_id: 11111111-1111-4111-8111-111111111111
                controller_id:
                status: bare-metal
                hostname: gpu-host-01
                facility_id: bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb
                ip: 203.0.113.10
                port: 22
                username: tenant
                ssh_endpoint: tenant@203.0.113.10
                gpu_type: H100
                gpu_count: 8
                cpu_type: Example 64-core CPU
                memory_bytes: 2199023255552
                storage_bytes: 30000000000000
                authorized_key_fingerprints:
                  - SHA256:exampleFingerprint0000000000000000000000000
                authorized_key_metadata:
                  - fingerprint: SHA256:exampleFingerprint0000000000000000000000000
                    label:
                    status: installed
                    linux_username: tenant
                    pushed_at: "2026-10-15T00:06:40Z"
                    failed_at:
                    failure_reason:
                    request_id:
                keys_pushed_at: "2026-10-15T00:06:40Z"
                enrolled_at: "2026-09-01T12:00:00Z"
                reboot:
                health_readiness:
                  ready: true
                  status: ready
                  reasons: []
                  advisory_reasons: []
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          description: The node is absent, is not in an owned reservation, is a cluster worker, or the ID is a VM id.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: node_not_found
                  message: Ornn could not find that node in this cluster.
        "409":
          description: The node's reservation has ended or cannot be used in its current state.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: reservation_not_launchable
                  message: This reservation cannot launch in its current state.
        default:
          $ref: "#/components/responses/Error"
  /v1/orchestrator/nodes/{node_id}/reboot:
    post:
      summary: Reboot or hard reset a Bare Metal host
      operationId: accessRebootNode
      tags:
        - Bare Metal
      description: >-
        Reboots a Bare Metal host. A reboot keeps your Linux user, home directory, SSH keys, and
        attached volumes. hard_reset wipes tenant data on the host (Linux user, home directory,
        authorized keys, and temporary files) and then reboots; Ornn restores the reservation's SSH
        keys afterward, so the reservation must have at least one. A reboot response includes
        reboot.status; poll the node until it is succeeded or failed, then confirm SSH. Another reboot
        request while one is in progress returns the existing reboot. Reuse the same request_id to
        retry safely without a second reboot. This route acts on hosts, not individual VMs: pass a
        node_id, never a VM id. To restart one VM, use POST
        /v1/orchestrator/nodes/{node_id}/virtual-machines/{vm_id}/reboot. Nodes in a Kubernetes or
        Slurm cluster cannot be rebooted here.
      parameters:
        - name: node_id
          in: path
          required: true
          description: A Bare Metal host node_id from the reservation's node list. Not a VM id.
          schema:
            type: string
            format: uuid
          example: 33333333-3333-4333-8333-333333333333
      requestBody:
        required: false
        description: Omit the body for a plain reboot.
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/AccessRebootRequest"
            example:
              mode: reboot
              request_id: reboot-2026-10-16-a
      responses:
        "200":
          description: The reboot or hard reset was queued. This does not mean the host is back.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AccessRebootResult"
              example:
                id: 33333333-3333-4333-8333-333333333333
                node_id: 33333333-3333-4333-8333-333333333333
                reservation_id: 11111111-1111-4111-8111-111111111111
                packet_id: 99999999-9999-4999-8999-999999999999
                mode: reboot
                reboot:
                  packet_id: 99999999-9999-4999-8999-999999999999
                  status: in_progress
                  started_at: "2026-10-16T14:00:00Z"
                  detail:
        "400":
          description: The mode is not reboot or hard_reset.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: invalid_reboot_mode
                  message: Choose reboot or hard reset.
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          description: The node is absent or not in an owned reservation.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: node_not_found
                  message: Ornn could not find that node in this cluster.
        "409":
          description: The node is a cluster worker, a reboot is already in progress, a hard reset has no SSH key to restore, or the reservation cannot be used in its current state.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: reboot_in_progress
                  message: This node is already rebooting. Wait for it to finish before requesting another action.
        "422":
          description: Ornn could not queue the reboot. Retry with the same request_id.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: reboot_enqueue_failed
                  message: Ornn could not send the reboot to this machine. Try again in a moment.
        default:
          $ref: "#/components/responses/Error"
  /v1/organizations/{organization_id}/ssh-keys:
    get:
      summary: List organization SSH keys
      operationId: accountListSshKeys
      tags:
        - SSH keys
      description: Lists SSH public keys registered to your organization, newest first. Use the organization ID returned by GET /v1/auth/session; an API key can read only its own organization. Pass status, limit, or cursor to page through keys (default page size 25, at most 500). Without any of these parameters the response contains every active and revoked key in one page. Requires an approved organization.
      parameters:
        - $ref: "#/components/parameters/AccountOrganizationId"
        - name: status
          in: query
          required: false
          description: Which keys to return. Passing this parameter turns on paging.
          schema:
            type: string
            enum:
              - active
              - revoked
              - all
            default: all
          example: active
        - name: limit
          in: query
          required: false
          description: Page size. Passing this parameter turns on paging.
          schema:
            type: integer
            minimum: 1
            maximum: 500
            default: 25
          example: 100
        - name: cursor
          in: query
          required: false
          description: Opaque next_cursor from the previous page. Keep the same status value; a cursor from a different status or organization is rejected.
          schema:
            type: string
            maxLength: 1024
      responses:
        "200":
          description: One page of organization SSH keys. This example is the last page.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AccountSshKeyPage"
              example:
                ssh_keys:
                  - id: 77777777-7777-4777-8777-777777777777
                    organization_id: 22222222-2222-4222-8222-222222222222
                    tenant_id: 22222222-2222-4222-8222-222222222222
                    public_key: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIExampleKeyMaterialForDocsOnly0000000000 dev@example.com
                    fingerprint: SHA256:ExampleFingerprintForDocumentationOnly000000
                    label: laptop
                    status: active
                    created_at: "2026-10-14T12:00:00Z"
                    revoked_at:
                next_cursor:
                has_revoked: false
        "400":
          description: Invalid status, page size, or cursor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: invalid_ssh_key_query
                  message: SSH key page size must be between 1 and 500.
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "429":
          $ref: "#/components/responses/AccountRateLimited"
        default:
          $ref: "#/components/responses/Error"
    post:
      summary: Add an organization SSH key
      operationId: accountCreateSshKey
      tags:
        - SSH keys
      description: Registers one SSH public key for your organization. Adding a key does not install it on any machine; assign it to a reservation or include it in a launch. If the organization already has an active key with the same fingerprint, the existing key is returned with outcome already_registered and your label is not applied. Adding a revoked key again reactivates it with outcome reactivated; reactivation does not push it to machines. Send only the public key, never the private key. Requires an approved organization.
      parameters:
        - $ref: "#/components/parameters/AccountOrganizationId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/AccountCreateSshKey"
            example:
              public_key: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIExampleKeyMaterialForDocsOnly0000000000 dev@example.com
              label: laptop
      responses:
        "201":
          description: The key was registered.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AccountSshKeyRegistration"
              example:
                id: 77777777-7777-4777-8777-777777777777
                organization_id: 22222222-2222-4222-8222-222222222222
                tenant_id: 22222222-2222-4222-8222-222222222222
                public_key: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIExampleKeyMaterialForDocsOnly0000000000 dev@example.com
                fingerprint: SHA256:ExampleFingerprintForDocumentationOnly000000
                label: laptop
                status: active
                created_at: "2026-10-14T12:00:00Z"
                revoked_at:
                outcome: created
        "200":
          description: The organization already had this key. outcome is already_registered (returned unchanged) or reactivated (a revoked key is active again).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AccountSshKeyRegistration"
              example:
                id: 77777777-7777-4777-8777-777777777777
                organization_id: 22222222-2222-4222-8222-222222222222
                tenant_id: 22222222-2222-4222-8222-222222222222
                public_key: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIExampleKeyMaterialForDocsOnly0000000000 dev@example.com
                fingerprint: SHA256:ExampleFingerprintForDocumentationOnly000000
                label: laptop
                status: active
                created_at: "2026-10-01T09:30:00Z"
                revoked_at:
                outcome: already_registered
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "409":
          description: A concurrent request registered the same fingerprint first. List keys and use the existing key.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail: An SSH key with this fingerprint is already registered.
        "422":
          description: The public key is empty, spans more than one line, uses an unsupported type, or is not a valid OpenSSH public key.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail: unsupported_ssh_key_type
        "429":
          $ref: "#/components/responses/AccountRateLimited"
        default:
          $ref: "#/components/responses/Error"
  /v1/organizations/{organization_id}/ssh-keys/{key_id}:
    delete:
      summary: Revoke an organization SSH key
      operationId: accountRevokeSshKey
      tags:
        - SSH keys
      description: Revokes one organization SSH key immediately. Ornn then makes a best-effort attempt, for up to 30 seconds before responding, to remove the key from your organization's VM and Bare Metal reservations and to queue the remaining keys for their machines. Removal takes effect when each machine applies the update; a successful response does not confirm removal from every machine. Existing SSH sessions stay connected. Add or push a replacement key before revoking a key you still need. Revoking an already revoked key returns 200 again. Requires an approved organization.
      parameters:
        - $ref: "#/components/parameters/AccountOrganizationId"
        - name: key_id
          in: path
          required: true
          description: Organization SSH key ID.
          schema:
            type: string
            format: uuid
          example: 77777777-7777-4777-8777-777777777777
      responses:
        "200":
          description: The key is revoked.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AccountSshKeyRevocation"
              example:
                ssh_key:
                  id: 77777777-7777-4777-8777-777777777777
                  organization_id: 22222222-2222-4222-8222-222222222222
                  tenant_id: 22222222-2222-4222-8222-222222222222
                  public_key: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIExampleKeyMaterialForDocsOnly0000000000 dev@example.com
                  fingerprint: SHA256:ExampleFingerprintForDocumentationOnly000000
                  label: laptop
                  status: revoked
                  created_at: "2026-10-14T12:00:00Z"
                  revoked_at: "2026-10-15T08:00:00Z"
                machines: []
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          description: The key does not exist in this organization.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail: SSH key not found.
        "429":
          $ref: "#/components/responses/AccountRateLimited"
        default:
          $ref: "#/components/responses/Error"
  /v1/orchestrator/nodes/reservations/{reservation_id}/ssh-keys:
    get:
      summary: List a reservation's SSH keys
      operationId: accountListReservationSshKeys
      tags:
        - SSH keys
      description: Returns the SSH keys that VM and Bare Metal machines in this reservation should accept. When anyone in your organization has assigned keys to the reservation, the list contains those active keys. When none are assigned, the reservation uses every active organization key and the list contains all of them. This is the desired key set, not proof of installation; check authorized_key_fingerprints on each node from GET /v1/orchestrator/nodes. Requires an approved organization and a reservation your organization holds.
      parameters:
        - $ref: "#/components/parameters/AccountReservationId"
      responses:
        "200":
          description: The reservation's current key set.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AccountReservationSshKeys"
              example:
                ssh_keys:
                  - id: 77777777-7777-4777-8777-777777777777
                    organization_id: 22222222-2222-4222-8222-222222222222
                    tenant_id: 22222222-2222-4222-8222-222222222222
                    public_key: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIExampleKeyMaterialForDocsOnly0000000000 dev@example.com
                    fingerprint: SHA256:ExampleFingerprintForDocumentationOnly000000
                    label: laptop
                    status: active
                    created_at: "2026-10-14T12:00:00Z"
                    revoked_at:
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/AccountReservationNotFound"
        "409":
          $ref: "#/components/responses/AccountReservationNotLaunchable"
        "429":
          $ref: "#/components/responses/AccountRateLimited"
        default:
          $ref: "#/components/responses/Error"
    post:
      summary: Assign an SSH key to a reservation
      operationId: accountAssignReservationSshKey
      tags:
        - SSH keys
      description: Assigns one key to a VM or Bare Metal reservation and queues the reservation's key set for its active machines. Send either the ID of an active organization key or a new public key; a new key is registered to your organization first. The first assignment replaces the default of every organization key, so afterwards only assigned keys are delivered. Queued is not installed; wait until the key's fingerprint appears in authorized_key_fingerprints on each node before connecting. You can assign keys before launch; they are used when machines start.
      parameters:
        - $ref: "#/components/parameters/AccountReservationId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/AccountAssignReservationSshKey"
            examples:
              existing:
                summary: Assign an existing organization key
                value:
                  ssh_key_id: 77777777-7777-4777-8777-777777777777
              new:
                summary: Register and assign a new public key
                value:
                  public_key: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIExampleKeyMaterialForDocsOnly0000000000 dev@example.com
                  label: laptop
      responses:
        "201":
          description: The key is assigned and delivery is queued for active machines. A request with a new public key also returns outcome.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AccountAssignedSshKey"
              example:
                id: 77777777-7777-4777-8777-777777777777
                organization_id: 22222222-2222-4222-8222-222222222222
                tenant_id: 22222222-2222-4222-8222-222222222222
                public_key: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIExampleKeyMaterialForDocsOnly0000000000 dev@example.com
                fingerprint: SHA256:ExampleFingerprintForDocumentationOnly000000
                label: laptop
                status: active
                created_at: "2026-10-14T12:00:00Z"
                revoked_at:
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          description: The organization is not approved, or ssh_key_id is not an active key in your organization.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: ssh_key_not_owned
                  message: That SSH key belongs to another account.
        "404":
          $ref: "#/components/responses/AccountReservationNotFound"
        "409":
          $ref: "#/components/responses/AccountReservationNotLaunchable"
        "422":
          description: The body is neither an ssh_key_id nor a public_key with an optional label.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          $ref: "#/components/responses/AccountRateLimited"
        default:
          $ref: "#/components/responses/Error"
  /v1/orchestrator/nodes/reservations/{reservation_id}/ssh-keys/{key_id}:
    delete:
      summary: Unassign an SSH key from a reservation
      operationId: accountUnassignReservationSshKey
      tags:
        - SSH keys
      description: Removes an assignment you created and queues the reservation's remaining key set for its active machines. An API key acts as its creator, so it can remove only assignments made by that person. If no assigned keys remain, the reservation returns to its default and every active organization key is queued. To stop a key from working everywhere, revoke it instead. Existing SSH sessions stay connected.
      parameters:
        - $ref: "#/components/parameters/AccountReservationId"
        - name: key_id
          in: path
          required: true
          description: Organization SSH key ID assigned to the reservation.
          schema:
            type: string
            format: uuid
          example: 77777777-7777-4777-8777-777777777777
      responses:
        "204":
          description: The assignment is removed and the remaining key set is queued.
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          description: The reservation is not visible to your organization, or you have no assignment for this key on it.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: reservation_ssh_key_not_found
                  message: That SSH key is not registered on this reservation. It may already be removed.
        "409":
          $ref: "#/components/responses/AccountReservationNotLaunchable"
        "429":
          $ref: "#/components/responses/AccountRateLimited"
        default:
          $ref: "#/components/responses/Error"
  /v1/orchestrator/nodes/{node_id}/push-keys:
    post:
      summary: Push SSH keys to a machine
      operationId: accountPushMachineSshKeys
      tags:
        - SSH keys
      description: Assigns the listed organization keys to the machine's reservation if needed, then queues the reservation's full key set for this machine. Pass the host node ID, which is a Bare Metal node's node_id or a VM's node_id from the reservation VM list (not the VM's own id). Ornn updates that host and the reservation's VMs on it. Use this to re-send keys after a machine was offline or a delivery failed. A 200 response means delivery was queued, not installed; poll the node and wait for the key's fingerprint in authorized_key_fingerprints. Machines in a Kubernetes or Slurm cluster are rejected.
      parameters:
        - name: node_id
          in: path
          required: true
          description: The host node ID (a Bare Metal node_id, or a VM's node_id) on a reservation your organization holds. A VM's own id returns 404 node_not_found.
          schema:
            type: string
            format: uuid
          example: 33333333-3333-4333-8333-333333333333
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/AccountPushSshKeys"
            example:
              ssh_key_ids:
                - 77777777-7777-4777-8777-777777777777
      responses:
        "200":
          description: Key delivery is queued for the machine.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AccountPushSshKeysResult"
              example:
                id: 33333333-3333-4333-8333-333333333333
                node_id: 33333333-3333-4333-8333-333333333333
                reservation_id: 11111111-1111-4111-8111-111111111111
                packet_id: 88888888-8888-4888-8888-888888888888
                packet_ids:
                  - 88888888-8888-4888-8888-888888888888
                ssh_key_ids:
                  - 77777777-7777-4777-8777-777777777777
        "400":
          description: ssh_key_ids is empty or repeats a key.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: ssh_key_ids_invalid
                  message: Choose at least one SSH key, each only once.
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          description: The organization is not approved, or a listed key is not an active key in your organization.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: ssh_key_not_owned
                  message: That SSH key belongs to another account.
        "404":
          description: The machine does not exist or is not in a reservation your organization holds.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "409":
          description: The machine is a Kubernetes or Slurm cluster worker, the VM is not running yet, or the reservation cannot accept changes.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: cluster_worker_keys_blocked
                  message: SSH keys cannot be pushed while this node is in a Kubernetes or Slurm cluster. Tear the cluster down first.
        "429":
          $ref: "#/components/responses/AccountRateLimited"
        default:
          $ref: "#/components/responses/Error"
  /v1/markets/spot/books:
    get:
      operationId: capacityListSpotBooks
      summary: List Spot order books
      tags:
        - Spot
      description: Returns one order book per GPU type with its best ask and best bid. Asks show what a buyer pays and bids show what a seller receives, so both already account for Ornn's fees. Prices are integer micro-USD per GPU-hour. Open to every member of your organization.
      responses:
        "200":
          description: Order books by GPU type.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CapacitySpotBookList"
              example:
                orderBooks:
                  - gpuSlug: nvidia_h100_sxm
                    orderBookId: bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb
                    bestAskPricePerGpuHourMicroUsd: 2650000
                    bestBidPricePerGpuHourMicroUsd: 2300000
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        default:
          $ref: "#/components/responses/Error"
  /v1/markets/spot/books/{gpuSlug}:
    get:
      operationId: capacityGetSpotBook
      summary: Get Spot order book depth
      tags:
        - Spot
      description: "Returns aggregated depth for one GPU type: GPUs and order counts at each price level, up to 50 levels per side. Depth never identifies who placed an order. A bid fills against one ask with room for all its GPUs, so a level with fewer free GPUs cannot fill a bid. Some asks sell only to 8-GPU bids; use a quote to see what a bid of your size would fill."
      parameters:
        - &id001
          name: gpuSlug
          in: path
          required: true
          description: Catalog GPU slug of the order book, from listSpotBooks.
          schema:
            type: string
          example: nvidia_h100_sxm
      responses:
        "200":
          description: Order book depth.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CapacitySpotDepth"
              example:
                gpuSlug: nvidia_h100_sxm
                orderBookId: bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb
                asks:
                  - pricePerGpuHourMicroUsd: 2650000
                    gpus: 16
                    orders: 2
                  - pricePerGpuHourMicroUsd: 2900000
                    gpus: 8
                    orders: 1
                bids:
                  - pricePerGpuHourMicroUsd: 2300000
                    gpus: 8
                    orders: 1
        "404":
          description: There is no order book for that GPU slug.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: order_book_not_found
                  message: There is no spot market for that GPU type.
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        default:
          $ref: "#/components/responses/Error"
  /v1/markets/spot/books/{gpuSlug}/trades:
    get:
      operationId: capacityListSpotTrades
      summary: List recent Spot trades
      tags:
        - Spot
      description: Returns up to 50 recent fills on one order book, newest first, with no identities.
      parameters:
        - *id001
      responses:
        "200":
          description: Recent trades.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CapacitySpotTradeList"
              example:
                gpuSlug: nvidia_h100_sxm
                trades:
                  - pricePerGpuHourMicroUsd: 2650000
                    gpus: 8
                    aggressorSide: bid
                    filledAt: "2026-10-14T18:30:00.000Z"
        "404":
          description: There is no order book for that GPU slug.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: order_book_not_found
                  message: There is no spot market for that GPU type.
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        default:
          $ref: "#/components/responses/Error"
  /v1/markets/spot/books/{gpuSlug}/quote:
    get:
      operationId: capacityQuoteSpotBid
      summary: Quote Spot bids
      tags:
        - Spot
      description: Previews what identical bids placed now would fill at, one after another, without placing anything. Each fill shows the price you would pay and when spot on its node is next scheduled to end. Your own organization's asks are skipped. The book can move before you bid; the quote is not a reservation of capacity.
      parameters:
        - *id001
        - name: gpus
          in: query
          required: true
          description: GPUs per VM.
          schema:
            type: integer
            enum:
              - 1
              - 2
              - 4
              - 8
          example: 8
        - name: orderType
          in: query
          required: true
          description: limit or market.
          schema:
            type: string
            enum:
              - limit
              - market
          example: limit
        - name: pricePerGpuHourMicroUsd
          in: query
          required: false
          description: "Required for limit: the most you would pay per GPU-hour, fees included."
          schema:
            type: integer
            format: int64
            minimum: 1
          example: 2700000
        - name: bids
          in: query
          required: false
          description: How many identical bids to quote, from 1 to 16. Defaults to 1.
          schema:
            type: integer
            minimum: 1
            maximum: 16
            default: 1
          example: 2
      responses:
        "200":
          description: Fills the bids would take now.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CapacitySpotQuote"
              example:
                gpuSlug: nvidia_h100_sxm
                fills:
                  - pricePerGpuHourMicroUsd: 2650000
                    interruptsAt: "2026-10-21T00:00:00.000Z"
                    interruptReason: reservation
                  - pricePerGpuHourMicroUsd: 2650000
                    interruptsAt: "2026-11-03T00:00:00.000Z"
                    interruptReason: listing_end
        "400":
          description: bids is outside 1 to 16.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: invalid_request
                  message: Check the request details and try again.
        "404":
          description: There is no order book for that GPU slug.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: order_book_not_found
                  message: There is no spot market for that GPU type.
        "422":
          description: gpus is not 1, 2, 4, or 8, orderType is unknown, or a limit has no positive price.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: invalid_gpus
                  message: Spot orders take 1, 2, 4, or 8 GPUs.
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        default:
          $ref: "#/components/responses/Error"
  /v1/markets/spot/orders:
    get:
      operationId: capacityListSpotOrders
      summary: List your Spot orders
      tags:
        - Spot
      description: Lists your organization's 200 most recent bids (resting, running, cancelled, and ended) and the asks your relisted capacity has on the books. Poll this or getSpotOrder to follow a bid.
      responses:
        "200":
          description: Your orders.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CapacitySpotOrderList"
              example:
                orders:
                  - &id002
                    orderId: aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa
                    orderBookId: bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb
                    gpuSlug: nvidia_h100_sxm
                    side: bid
                    orderType: limit
                    pricePerGpuHourMicroUsd: 2500000
                    requestedGpus: 8
                    remainingGpus: 0
                    status: running
                    cancelReason:
                    aggressorSide: bid
                    listingId: eeeeeeee-eeee-4eee-8eee-eeeeeeeeeeee
                    nodeId: 33333333-3333-4333-8333-333333333333
                    createdAt: "2026-10-14T18:30:00.000Z"
                    filledAt: "2026-10-14T18:30:00.000Z"
                    endedAt:
                    endReason:
                    isMine: true
                    isSeller: false
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        default:
          $ref: "#/components/responses/Error"
    post:
      operationId: capacityPlaceSpotBid
      summary: Place a Spot bid
      tags:
        - Spot
      description: |-
        Bids for one VM with 1, 2, 4, or 8 GPUs on one node. A limit bid fills now against the lowest ask at or below your price, fees included, or rests on the book; a market bid fills now or is cancelled with cancelReason unfilled_market_order. Each bid fills against one ask with room for all its GPUs and never against your own organization's capacity. Some asks sell only to 8-GPU bids, and a 2- or 4-GPU bid also needs a matching group of GPUs free on the node.

        A filled bid has status running and becomes a spot reservation with the same ID. The reservation appears shortly after the fill; poll GET /commerce/reservations/{reservationId} using the orderId. Each hour is prepaid when it starts, from Ornn credits first and then your saved card. The bid is not refused for a missing card, but if the first or a later hour cannot be paid the reservation ends with endReason payment_failed. Save a card in the console before bidding unless your credits cover the hours.

        Requires Idempotency-Key. To buy several VMs, place one bid per VM, each with its own key. Available to every member of your organization; approval is not required.
      parameters:
        - &id006
          name: Idempotency-Key
          in: header
          required: true
          description: Required. A unique key of 1 to 200 characters for this operation, such as a UUID. Retrying with the same key and the same body returns the original result with HTTP 200 instead of creating a second one. The key is remembered for 24 hours per organization; reusing it for a different request returns 409 idempotency_conflict. A missing key returns 400 idempotency_key_required.
          schema:
            type: string
            minLength: 1
            maxLength: 200
          example: 3f1c2a9e-6b1d-4c55-9a77-0d2b8f1e4c10
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CapacitySpotBidRequest"
            example:
              gpuSlug: nvidia_h100_sxm
              gpus: 8
              orderType: limit
              pricePerGpuHourMicroUsd: 2700000
      responses:
        "201":
          description: "Bid placed. Check order.status: running (filled), resting, or cancelled."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CapacitySpotOrderEnvelope"
              example:
                order: *id002
        "200":
          description: A retry with the same Idempotency-Key and body; returns the original order's current state.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CapacitySpotOrderEnvelope"
              example:
                order:
                  orderId: aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa
                  orderBookId: bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb
                  gpuSlug: nvidia_h100_sxm
                  side: bid
                  orderType: limit
                  pricePerGpuHourMicroUsd: 2500000
                  requestedGpus: 8
                  remainingGpus: 8
                  status: resting
                  cancelReason:
                  aggressorSide:
                  listingId:
                  nodeId:
                  createdAt: "2026-10-14T18:30:00.000Z"
                  filledAt:
                  endedAt:
                  endReason:
                  isMine: true
                  isSeller: false
        "400":
          description: Missing Idempotency-Key or invalid body.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: idempotency_key_required
                  message: Send an Idempotency-Key header with this request.
        "404":
          description: There is no order book for that GPU slug.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: order_book_not_found
                  message: There is no spot market for that GPU type.
        "409":
          description: The Idempotency-Key was already used for a different request.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: idempotency_conflict
                  message: That request key was already used for a different request.
        "422":
          description: gpus is not 1, 2, 4, or 8, orderType is unknown, or a limit has no positive price.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: invalid_gpus
                  message: Spot orders take 1, 2, 4, or 8 GPUs.
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        default:
          $ref: "#/components/responses/Error"
  /v1/markets/spot/orders/{orderId}:
    get:
      operationId: capacityGetSpotOrder
      summary: Get a Spot order
      tags:
        - Spot
      description: Returns one order your organization placed, or one that filled against capacity you listed.
      parameters:
        - &id003
          name: orderId
          in: path
          required: true
          description: Spot order ID. For a filled bid this is also its reservation ID.
          schema: &id004
            type: string
            format: uuid
          example: aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa
      responses:
        "200":
          description: The order.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CapacitySpotOrderEnvelope"
              example:
                order: *id002
        "404":
          description: The order does not exist or is not visible to your organization.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: order_not_found
                  message: Ornn could not find that order.
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        default:
          $ref: "#/components/responses/Error"
    delete:
      operationId: capacityCancelSpotOrder
      summary: Cancel a bid or end a spot reservation
      tags:
        - Spot
      description: |-
        Cancels a resting bid, or ends a running spot reservation and stops its VM. The buyer can cancel or end its own bids. The organization that listed the node can end a running bid on it (endReason stopped_by_seller). Repeating the call on a cancelled or ended order returns its current state. Asks that your listings place on the book cannot be cancelled here; delist the listing instead.

        A running reservation is billed for the time it ran; the unused part of the current hour returns as Ornn credits. The VM stops asynchronously after the order ends.
      parameters:
        - *id003
      responses:
        "200":
          description: "The order after cancellation: status cancelled (resting bid) or ended (running bid)."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CapacitySpotOrderEnvelope"
              example:
                order:
                  orderId: aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa
                  orderBookId: bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb
                  gpuSlug: nvidia_h100_sxm
                  side: bid
                  orderType: limit
                  pricePerGpuHourMicroUsd: 2500000
                  requestedGpus: 8
                  remainingGpus: 0
                  status: ended
                  cancelReason:
                  aggressorSide: bid
                  listingId: eeeeeeee-eeee-4eee-8eee-eeeeeeeeeeee
                  nodeId: 33333333-3333-4333-8333-333333333333
                  createdAt: "2026-10-14T18:30:00.000Z"
                  filledAt: "2026-10-14T18:30:00.000Z"
                  endedAt: "2026-10-14T21:05:00.000Z"
                  endReason: stopped_by_buyer
                  isMine: true
                  isSeller: false
        "404":
          description: The order does not exist or your organization cannot cancel it.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: order_not_found
                  message: Ornn could not find that order.
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        default:
          $ref: "#/components/responses/Error"
  /v1/markets/schedules:
    get:
      operationId: capacityListSchedules
      summary: List tradable listings
      tags:
        - Market
        - Reserve
      description: Lists listings that can be traded now, with sold windows, free capacity over time, and terms. Use listingKind secondary for the Market (relisted capacity, open to every member of your organization) and primary for Reserve (shown only to approved organizations; otherwise silently omitted). Listings offered only on Spot are not included. Prices are what you pay, fees included. The response does not include offers; read one listing for its offers.
      parameters:
        - name: listingKind
          in: query
          required: false
          description: primary (Reserve) or secondary (Market). Omit for both.
          schema:
            type: string
            enum:
              - primary
              - secondary
          example: secondary
        - name: gpuSlug
          in: query
          required: false
          description: Only listings for this catalog GPU slug.
          schema:
            type: string
          example: nvidia_h100_sxm
        - name: from
          in: query
          required: false
          description: RFC 3339 time. Only listings that end after it.
          schema:
            type: string
            format: date-time
          example: "2026-10-20T00:00:00Z"
        - name: to
          in: query
          required: false
          description: RFC 3339 time. Only listings that start at or before it.
          schema:
            type: string
            format: date-time
          example: "2026-11-20T00:00:00Z"
      responses:
        "200":
          description: Tradable listings.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CapacityScheduleList"
              example:
                schedules:
                  - &id005
                    listingId: eeeeeeee-eeee-4eee-8eee-eeeeeeeeeeee
                    listingKind: secondary
                    gpuSlug: nvidia_h100_sxm
                    gpusPerNode: 8
                    nodeCount: 2
                    nodeIds:
                      - 33333333-3333-4333-8333-333333333333
                      - 12121212-1212-4212-8212-121212121212
                    startsAt: "2026-10-20T00:00:00Z"
                    endsAt: "2026-11-03T00:00:00Z"
                    status: active
                    cancelReason:
                    acceptedWindows:
                      - startsAt: "2026-10-21T00:00:00.000Z"
                        endsAt: "2026-10-23T00:00:00.000Z"
                        isMine: false
                    capacityWindows:
                      - startsAt: "2026-10-20T00:00:00.000Z"
                        endsAt: "2026-10-21T00:00:00.000Z"
                        nodeCount: 2
                      - startsAt: "2026-10-21T00:00:00.000Z"
                        endsAt: "2026-10-23T00:00:00.000Z"
                        nodeCount: 1
                      - startsAt: "2026-10-23T00:00:00.000Z"
                        endsAt: "2026-11-03T00:00:00.000Z"
                        nodeCount: 2
                    typedTerms: true
                    terms:
                      - listingTermId: 88888888-8888-4888-8888-888888888888
                        type: reserved
                        minDurationSeconds: 86400
                        maxDurationSeconds: 604800
                        pricePerGpuHourMicroUsd: 4200000
                        downPaymentCents:
                        renewable: false
                    askPricePerGpuHourMicroUsd: 4200000
                    spotPricePerGpuHourMicroUsd: 4200000
                    spotGpusInUse: 8
                    spotGpusOffered: 0
                    isMine: false
        "400":
          description: Unknown listingKind or an invalid time.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: invalid_request
                  message: Check the request details and try again.
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        default:
          $ref: "#/components/responses/Error"
  /v1/markets/schedules/{listingId}:
    get:
      operationId: capacityGetSchedule
      summary: Get a tradable listing
      tags:
        - Market
        - Reserve
      description: Returns one listing as it trades, including its offers. Read it immediately before an offer or purchase to get current terms, prices, and free capacity. Reserve listings return 404 unless your organization is approved or owns the listing.
      parameters:
        - name: listingId
          in: path
          required: true
          description: Listing ID.
          schema: *id004
          example: eeeeeeee-eeee-4eee-8eee-eeeeeeeeeeee
      responses:
        "200":
          description: The listing.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CapacityScheduleEnvelope"
              example:
                schedule: *id005
        "404":
          description: The listing is not tradable or not visible to your organization.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: schedule_not_found
                  message: Ornn could not find that listing.
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        default:
          $ref: "#/components/responses/Error"
  /v1/markets/schedules/purchases:
    post:
      operationId: capacityBuyWindows
      summary: Buy Market windows
      tags:
        - Market
      description: |-
        Buys one or more windows of relisted capacity at the asking price. There is no offer and no seller approval: every leg is bought together or none are. Each leg becomes its own reservation, returned as reservationId.

        Read the listing first. Choose a reserved term whose duration bounds allow your window (a shorter window is allowed when it ends exactly at the listing's end) and send its listingTermId and pricePerGpuHourMicroUsd as askPricePerGpuHourMicroUsd. Use whole-second times; windows can start up to five minutes in the past.

        Payment is up front. A reservation under $10,000 is charged at once from Ornn credits, then your saved card; if that charge fails, the purchase is cancelled and the window returns to the Market. A reservation of $10,000 or more is invoiced by wire and is held until paid. Poll GET /commerce/reservations/{reservationId}: status moves from awaiting_payment to confirmed, then active when the window starts. The reservation can take a moment to appear.

        Requires Idempotency-Key. Available to every member of your organization; approval is not required. You cannot buy your own organization's listings.
      parameters:
        - *id006
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CapacityPurchaseRequest"
            example:
              legs:
                - listingId: eeeeeeee-eeee-4eee-8eee-eeeeeeeeeeee
                  listingTermId: 88888888-8888-4888-8888-888888888888
                  nodeCount: 1
                  startsAt: "2026-10-24T00:00:00Z"
                  endsAt: "2026-10-27T00:00:00Z"
                  askPricePerGpuHourMicroUsd: 4200000
      responses:
        "201":
          description: Windows bought. Each purchase is a reservation awaiting payment.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CapacityPurchaseList"
              example:
                purchases:
                  - reservationId: dddddddd-dddd-4ddd-8ddd-dddddddddddd
                    listingId: eeeeeeee-eeee-4eee-8eee-eeeeeeeeeeee
                    nodeCount: 1
                    startsAt: "2026-10-24T00:00:00.000Z"
                    endsAt: "2026-10-27T00:00:00.000Z"
                    askPricePerGpuHourMicroUsd: 4200000
        "200":
          description: A retry with the same Idempotency-Key and body; returns the original purchases.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CapacityPurchaseList"
              example:
                purchases:
                  - reservationId: dddddddd-dddd-4ddd-8ddd-dddddddddddd
                    listingId: eeeeeeee-eeee-4eee-8eee-eeeeeeeeeeee
                    nodeCount: 1
                    startsAt: "2026-10-24T00:00:00.000Z"
                    endsAt: "2026-10-27T00:00:00.000Z"
                    askPricePerGpuHourMicroUsd: 4200000
        "400":
          description: Missing Idempotency-Key, no legs, more than 50 legs, or an invalid body.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: idempotency_key_required
                  message: Send an Idempotency-Key header with this request.
        "403":
          description: The listing belongs to your organization.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: cannot_buy_own_listing
                  message: You cannot buy your own listing.
        "404":
          description: The listing is not tradable.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: schedule_not_found
                  message: Ornn could not find that listing.
        "409":
          description: The price changed, part of the window sold, the listing closed, or the key was reused. Nothing was bought; read the listing again before retrying with a new key.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: ask_changed
                  message: The asking price changed. Review the new price and try again.
        "422":
          description: The window, term, duration, or node count is not valid for the listing. Codes include invalid_listing_term, invalid_duration, invalid_window, purchase_too_short, purchase_in_past, purchase_outside_listing, invalid_node_count, purchase_legs_overlap, and primary_sells_by_offer.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: invalid_listing_term
                  message: Choose an available listing term.
        "401":
          $ref: "#/components/responses/Unauthorized"
        default:
          $ref: "#/components/responses/Error"
  /v1/markets/schedules/{listingId}/offers:
    get:
      operationId: capacityListListingOffers
      summary: List offers on a listing
      tags:
        - Reserve
      description: Lists offers posted on one Reserve listing, in every status. Offers never identify the organization that posted them; isMine marks yours.
      parameters:
        - &id007
          name: listingId
          in: path
          required: true
          description: Listing ID (capacityListingId or listingId).
          schema: *id004
          example: 77777777-7777-4777-8777-777777777777
      responses:
        "200":
          description: Offers on the listing.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CapacityOfferList"
              example:
                offers:
                  - &id008
                    offerId: 99999999-9999-4999-8999-999999999999
                    listingId: 77777777-7777-4777-8777-777777777777
                    listingTermId: 88888888-8888-4888-8888-888888888888
                    startsAt: "2026-10-15T00:00:00.000Z"
                    endsAt: "2026-11-14T00:00:00.000Z"
                    pricePerGpuHourMicroUsd: 2400000
                    downPaymentCents: 500000
                    status: open
                    cancelReason:
                    createdAt: "2026-10-14T18:30:00.000Z"
                    acceptedAt:
                    isMine: true
                    onMyListing: false
        "404":
          description: The listing is not tradable or not visible to your organization.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: schedule_not_found
                  message: Ornn could not find that listing.
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        default:
          $ref: "#/components/responses/Error"
    post:
      operationId: capacityPostOffer
      summary: Post a Reserve offer
      tags:
        - Reserve
      description: |-
        Offers a price for a whole Reserve listing: every node, starting at the later of the listing's start and now, for the chosen duration. The listing owner accepts or declines. Your organization must be approved.

        When the listing has terms, send listingTermId for a reserved term and durationSeconds within its bounds (optional when the minimum equals the maximum). The offer is open until it is accepted, you cancel it, or another trade takes the window. Relisted (Market) capacity does not take offers.

        Requires Idempotency-Key.
      parameters:
        - *id007
        - *id006
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CapacityOfferRequest"
            example:
              listingTermId: 88888888-8888-4888-8888-888888888888
              durationSeconds: 2592000
              pricePerGpuHourMicroUsd: 2400000
              downPaymentCents: 500000
      responses:
        "201":
          description: Offer posted.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CapacityOfferEnvelope"
              example:
                offer: *id008
        "200":
          description: A retry with the same Idempotency-Key and body; returns the original offer.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CapacityOfferEnvelope"
              example:
                offer: *id008
        "400":
          description: Missing Idempotency-Key or invalid body.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: idempotency_key_required
                  message: Send an Idempotency-Key header with this request.
        "403":
          description: The listing belongs to your organization.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: cannot_offer_on_own_listing
                  message: You cannot make an offer on your own listing.
        "404":
          description: The listing does not exist, or your organization is not approved.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: schedule_not_found
                  message: Ornn could not find that listing.
        "409":
          description: The window is already sold, the listing closed, or the key was reused.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: window_unavailable
                  message: Part of this window is already reserved.
        "422":
          description: The term, duration, price, or down payment is not valid. Codes include invalid_listing_term, invalid_duration, offer_below_minimum, invalid_price, invalid_down_payment, and secondary_sells_at_ask.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: invalid_listing_term
                  message: Choose an available listing term.
        "401":
          $ref: "#/components/responses/Unauthorized"
        default:
          $ref: "#/components/responses/Error"
  /v1/markets/schedules/{listingId}/offers/{offerId}:
    delete:
      operationId: capacityCancelOffer
      summary: Cancel an offer
      tags:
        - Reserve
      description: Cancels an open offer. The organization that posted it can withdraw it, and the listing owner can decline it. Cancelling an offer that is no longer open returns its current state.
      parameters:
        - *id007
        - &id009
          name: offerId
          in: path
          required: true
          description: Offer ID.
          schema: *id004
          example: 99999999-9999-4999-8999-999999999999
      responses:
        "200":
          description: The offer after cancellation.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CapacityOfferEnvelope"
              example:
                offer:
                  offerId: 99999999-9999-4999-8999-999999999999
                  listingId: 77777777-7777-4777-8777-777777777777
                  listingTermId: 88888888-8888-4888-8888-888888888888
                  startsAt: "2026-10-15T00:00:00.000Z"
                  endsAt: "2026-11-14T00:00:00.000Z"
                  pricePerGpuHourMicroUsd: 2400000
                  downPaymentCents: 500000
                  status: cancelled
                  cancelReason: cancelled_by_author
                  createdAt: "2026-10-14T18:30:00.000Z"
                  acceptedAt:
                  isMine: true
                  onMyListing: false
        "404":
          description: The offer does not exist or your organization cannot cancel it.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: offer_not_found
                  message: Ornn could not find that offer.
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        default:
          $ref: "#/components/responses/Error"
  /v1/markets/schedules/{listingId}/offers/{offerId}/accept:
    post:
      operationId: capacityAcceptOffer
      summary: Accept an offer on your listing
      tags:
        - Reserve
      description: "For organizations that own a Reserve listing. Accepting sells the offer's window to the buyer: the window starts at the later of the offer's start and now. The buyer's reservation has the offer's ID. Other open offers that overlap the window are cancelled. Repeating the call on an accepted offer returns it again. No request body is needed."
      parameters:
        - *id007
        - *id009
      responses:
        "200":
          description: Offer accepted.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CapacityOfferAccepted"
              example:
                offer:
                  offerId: 99999999-9999-4999-8999-999999999999
                  listingId: 77777777-7777-4777-8777-777777777777
                  listingTermId: 88888888-8888-4888-8888-888888888888
                  startsAt: "2026-10-15T00:00:00.000Z"
                  endsAt: "2026-11-14T00:00:00.000Z"
                  pricePerGpuHourMicroUsd: 2400000
                  downPaymentCents: 500000
                  status: accepted
                  cancelReason:
                  createdAt: "2026-10-14T18:30:00.000Z"
                  acceptedAt: "2026-10-15T09:00:00.000Z"
                  isMine: false
                  onMyListing: true
                reservationId: 99999999-9999-4999-8999-999999999999
        "404":
          description: The offer does not exist or the listing is not yours.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: offer_not_found
                  message: Ornn could not find that offer.
        "409":
          description: The offer is no longer open, the listing closed, or the window sold.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: offer_not_open
                  message: This offer is no longer open.
        "422":
          description: The offer's term or price is no longer valid for the listing.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: offer_below_minimum
                  message: Offer more per GPU-hour for this listing.
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        default:
          $ref: "#/components/responses/Error"
  /v1/markets/offers:
    get:
      operationId: capacityListMyOffers
      summary: List your offers
      tags:
        - Reserve
      description: Lists the 200 most recent offers your organization posted, in every status.
      responses:
        "200":
          description: Your offers.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CapacityOfferList"
              example:
                offers:
                  - *id008
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        default:
          $ref: "#/components/responses/Error"
  /commerce/v2/listings:
    get:
      operationId: capacityListListings
      summary: List capacity listings
      tags:
        - Reserve
      description: "Lists capacity listings visible to your organization, newest first: available, unexpired listings (Reserve listings only when your organization is approved), listings your organization owns such as its relists, and listings it holds a reservation on. Returns hardware, size, and availability; read prices from the listing's terms. Listings never identify who supplies the capacity. Follow nextCursor until it is absent."
      parameters:
        - name: kind
          in: query
          required: false
          description: primary for Reserve listings or secondary for relisted capacity.
          schema:
            type: string
            enum:
              - primary
              - secondary
              - on_demand
          example: primary
        - name: state
          in: query
          required: false
          description: Filter by state.
          schema:
            type: string
            enum:
              - draft
              - available
              - held
              - sold
              - delisted
        - name: sourceReservationId
          in: query
          required: false
          description: Relists of a reservation your organization holds.
          schema: *id004
        - name: organizationId
          in: query
          required: false
          description: Your own organization ID, to list only listings your organization owns. Any other value returns 403.
          schema: *id004
        - name: ids
          in: query
          required: false
          description: Comma-separated listing IDs, at most 200.
          schema:
            type: string
        - name: limit
          in: query
          required: false
          description: Page size from 1 to 100. Defaults to 50.
          schema:
            type: integer
            minimum: 1
            maximum: 100
          example: 50
        - name: cursor
          in: query
          required: false
          description: The nextCursor from the previous page, unchanged.
          schema:
            type: string
      responses:
        "200":
          description: One page of listings.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CapacityListingPage"
              example:
                listings:
                  - &id010
                    capacityListingId: 77777777-7777-4777-8777-777777777777
                    listingNumber: 1042
                    parentReservationId:
                    kind: primary
                    state: available
                    gpuSlug: nvidia_h100_sxm
                    gpusPerNode: 8
                    capacityNodeCount: 4
                    facilityId: cccccccc-cccc-4ccc-8ccc-cccccccccccc
                    provisioningType: bare-metal
                    availableAt: "2026-10-15T00:00:00Z"
                    availableUntil: "2027-10-15T00:00:00Z"
                    daysToRfs:
                    cpu: 2x 56-core x86
                    ram: 2 TB
                    storage: 8x 3.84 TB NVMe
                    fabricType: InfiniBand 3.2 Tbps
                    internet: 10 Gbps
                    createdAt: "2026-10-01T12:00:00Z"
                    updatedAt: "2026-10-01T12:00:00Z"
        "400":
          description: Invalid ID, limit, or cursor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: invalid_limit
                  message: Choose a page size between 1 and 100.
        "403":
          description: organizationId or sourceReservationId belongs to another organization.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: forbidden
                  message: Your account cannot make this change.
        "401":
          $ref: "#/components/responses/Unauthorized"
        default:
          $ref: "#/components/responses/Error"
  /commerce/v2/listings/{listingId}:
    get:
      operationId: capacityGetListing
      summary: Get a capacity listing
      tags:
        - Reserve
      description: Returns one listing's hardware, size, and availability. A listing that is not available, has expired, or is a Reserve listing while your organization is not approved returns 404, unless your organization owns it or holds a reservation on it.
      parameters:
        - *id007
      responses:
        "200":
          description: The listing.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CapacityListing"
              example: *id010
        "400":
          description: The ID is not a UUID.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: invalid_id
                  message: That capacity or financial term ID is not valid.
        "404":
          description: The listing is not visible to your organization.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: listing_not_found
                  message: This capacity listing is not available.
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        default:
          $ref: "#/components/responses/Error"
    patch:
      operationId: capacityUpdateListing
      summary: Pause, resume, or withdraw your listing
      tags:
        - Relist
      description: "Changes the state of a listing your organization owns. On a relist only state can change: capacity, hardware specifications, and dates are fixed and any change to them returns 409 listing_reserved. Moving a listing to draft or delisted is refused while a non-Spot reservation on it is active. Taking a listing off sale stops new sales and ends running Spot VMs on it; windows already sold stay sold."
      parameters:
        - *id007
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CapacityListingUpdate"
            example:
              state: draft
      responses:
        "200":
          description: The updated listing.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CapacityListing"
              example:
                capacityListingId: eeeeeeee-eeee-4eee-8eee-eeeeeeeeeeee
                listingNumber: 1188
                parentReservationId: 11111111-1111-4111-8111-111111111111
                kind: secondary
                state: draft
                gpuSlug: nvidia_h100_sxm
                gpusPerNode: 8
                capacityNodeCount: 2
                facilityId: cccccccc-cccc-4ccc-8ccc-cccccccccccc
                provisioningType: virtual-machine
                availableAt: "2026-10-20T00:00:00Z"
                availableUntil: "2026-11-03T00:00:00Z"
                daysToRfs:
                cpu: 2x 56-core x86
                ram: 2 TB
                storage: 8x 3.84 TB NVMe
                fabricType: InfiniBand 3.2 Tbps
                internet: 10 Gbps
                createdAt: "2026-10-14T18:30:00Z"
                updatedAt: "2026-10-14T18:30:00Z"
        "400": &id011
          description: Malformed body, unknown field, or invalid ID.
          content:
            application/json:
              schema:
                anyOf:
                  - $ref: "#/components/schemas/Error"
                  - $ref: "#/components/schemas/CapacityRequestBodyError"
              example:
                detail:
                  code: invalid_id
                  message: That capacity or financial term ID is not valid.
        "403":
          description: Your organization does not own the listing.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: forbidden
                  message: Your account cannot make this change.
        "409":
          description: A non-Spot reservation is active, or a fixed field changed.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: listing_has_active_reservations
                  message: This listing cannot be withdrawn while a non-Spot reservation is active. Wait until it ends and try again.
        "422":
          description: Unknown state.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: invalid_state
                  message: Choose draft, available, or delisted.
        "401":
          $ref: "#/components/responses/Unauthorized"
        default:
          $ref: "#/components/responses/Error"
  /commerce/v2/listings/{listingId}/terms:
    get:
      operationId: capacityListListingTerms
      summary: List a listing's terms
      tags:
        - Reserve
      description: "Returns the listing's financial terms: duration bounds, price, down payment, and renewal. Visibility matches getListing. On relisted capacity these prices are the seller's asking price before fees; Market buyers pay the price in the tradable listing's terms."
      parameters:
        - *id007
      responses:
        "200":
          description: The listing's terms.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CapacityListingTermList"
              example:
                terms:
                  - listingTermsId: 88888888-8888-4888-8888-888888888888
                    capacityListingId: 77777777-7777-4777-8777-777777777777
                    type: reserved
                    renewable: false
                    minDurationHours: 720
                    maxDurationHours: 2160
                    downPaymentCents: 500000
                    pricePerGpuHourMicroUsd: 2500000
                    createdAt: "2026-10-01T12:00:00Z"
        "404":
          description: The listing is not visible to your organization.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: listing_not_found
                  message: This capacity listing is not available.
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        default:
          $ref: "#/components/responses/Error"
    post:
      operationId: capacityAddListingTerm
      summary: Add a term to your listing
      tags:
        - Relist
      description: Adds a financial term to a draft or available listing your organization owns, for example a spot term on a VM relist that sells reserved windows. Terms cannot be edited; add a new one and delete the old one. Requires Idempotency-Key.
      parameters:
        - *id007
        - &id012
          name: Idempotency-Key
          in: header
          required: true
          description: Required. A unique key of 1 to 200 bytes for this operation, such as a UUID. Retrying with the same key and the same body returns the original response instead of writing again. The key is remembered for 24 hours; reusing it with a different body returns 409 idempotency_conflict. A missing key returns 422 invalid_idempotency_key.
          schema:
            type: string
            minLength: 1
            maxLength: 200
          example: 3f1c2a9e-6b1d-4c55-9a77-0d2b8f1e4c10
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CapacityTermInput"
            example:
              type: spot
              minDurationHours: 1
              maxDurationHours: 1
              pricePerGpuHourMicroUsd: 2500000
              downPaymentCents:
              renewable: true
      responses:
        "201":
          description: Term added.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CapacityListingTerm"
              example:
                listingTermsId: 13131313-1313-4313-8313-131313131313
                capacityListingId: eeeeeeee-eeee-4eee-8eee-eeeeeeeeeeee
                type: spot
                renewable: true
                minDurationHours: 1
                maxDurationHours: 1
                downPaymentCents:
                pricePerGpuHourMicroUsd: 2500000
                createdAt: "2026-10-14T18:30:00Z"
        "400": *id011
        "403":
          description: Your organization does not own the listing.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: forbidden
                  message: Your account cannot make this change.
        "409":
          description: The listing is not draft or available, or the key was reused.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: listing_not_editable
                  message: Add terms only to draft or available listings.
        "422":
          description: Invalid term, or a missing Idempotency-Key. Codes include invalid_terms, invalid_hourly_terms, invalid_duration, invalid_spot_listing, and invalid_idempotency_key.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: invalid_hourly_terms
                  message: Spot and On-demand terms require a one-hour duration with renewal enabled.
        "401":
          $ref: "#/components/responses/Unauthorized"
        default:
          $ref: "#/components/responses/Error"
  /commerce/v2/listings/{listingId}/terms/{listingTermsId}:
    delete:
      operationId: capacityDeleteListingTerm
      summary: Delete a term from your listing
      tags:
        - Relist
      description: Deletes an unused term from a listing your organization owns. A term used by a reservation or another retained record cannot be deleted.
      parameters:
        - *id007
        - name: listingTermsId
          in: path
          required: true
          description: The term's listingTermsId.
          schema: *id004
          example: 13131313-1313-4313-8313-131313131313
      responses:
        "204":
          description: Term deleted.
        "403":
          description: Your organization does not own the listing.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: forbidden
                  message: Your account cannot make this change.
        "404":
          description: The term does not belong to this listing.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: term_not_found
                  message: This listing term is not available.
        "409":
          description: The term is in use.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: term_in_use
                  message: This term is still in use and cannot be deleted.
        "401":
          $ref: "#/components/responses/Unauthorized"
        default:
          $ref: "#/components/responses/Error"
  /commerce/v2/listings/{listingId}/delist:
    post:
      operationId: capacityDelistListing
      summary: Delist your listing
      tags:
        - Relist
      description: Withdraws a listing your organization owns from new sales. Its unsold time leaves the Market, its Spot asks are cancelled, and running Spot VMs on it end. Windows already sold stay sold. Refused while a non-Spot reservation on the listing is active. No request body is needed.
      parameters:
        - *id007
      responses:
        "200":
          description: The delisted listing.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CapacityListing"
              example:
                capacityListingId: eeeeeeee-eeee-4eee-8eee-eeeeeeeeeeee
                listingNumber: 1188
                parentReservationId: 11111111-1111-4111-8111-111111111111
                kind: secondary
                state: delisted
                gpuSlug: nvidia_h100_sxm
                gpusPerNode: 8
                capacityNodeCount: 2
                facilityId: cccccccc-cccc-4ccc-8ccc-cccccccccccc
                provisioningType: virtual-machine
                availableAt: "2026-10-20T00:00:00Z"
                availableUntil: "2026-11-03T00:00:00Z"
                daysToRfs:
                cpu: 2x 56-core x86
                ram: 2 TB
                storage: 8x 3.84 TB NVMe
                fabricType: InfiniBand 3.2 Tbps
                internet: 10 Gbps
                createdAt: "2026-10-14T18:30:00Z"
                updatedAt: "2026-10-14T18:30:00Z"
        "403":
          description: Your organization does not own the listing.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: forbidden
                  message: Your account cannot make this change.
        "409":
          description: A non-Spot reservation on the listing is active.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: listing_has_active_reservations
                  message: This listing cannot be withdrawn while a non-Spot reservation is active. Wait until it ends and try again.
        "401":
          $ref: "#/components/responses/Unauthorized"
        default:
          $ref: "#/components/responses/Error"
  /commerce/v2/listings/{listingId}/nodes:
    get:
      operationId: capacityListListingNodes
      summary: List your listing's nodes
      tags:
        - Relist
      description: Returns the node IDs behind a listing your organization owns. A relist of a future reservation returns nodes only after machines are assigned.
      parameters:
        - *id007
      responses:
        "200":
          description: Node IDs.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CapacityListingNodes"
              example:
                nodeIds:
                  - 33333333-3333-4333-8333-333333333333
                  - 12121212-1212-4212-8212-121212121212
        "403":
          description: Your organization does not own the listing.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: forbidden
                  message: Your account cannot make this change.
        "404":
          description: The listing does not exist.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: listing_not_found
                  message: Ornn could not find that listing.
        "401":
          $ref: "#/components/responses/Unauthorized"
        default:
          $ref: "#/components/responses/Error"
  /commerce/reservations/{reservationId}/relist:
    post:
      operationId: capacityRelistReservation
      summary: Relist capacity from a reservation
      tags:
        - Relist
      description: |-
        Creates a listing that sells whole nodes from a reservation your organization holds, at your asking price, and puts it on sale immediately. Your reservation and its billing continue; buyers never see who relisted the capacity.

        Read the reservation first: relistableNodeCount, relistableNodeIds, and relistUnavailableReason describe what you can relist. The window must fit inside the reservation. An end may be null or omitted only when the source has no committed expiry; renewable reservations, including month-to-month contracts, cannot be relisted even if they have a scheduled end. Use a reserved term to sell windows in the Market (set minDurationHours to the shortest rental you accept and maxDurationHours no longer than the window), or a spot term (1 hour, renewable) to offer VM capacity on the Spot order book. You can relist the same reservation again as long as no two live relists cover the same node at the same time. Spot and On-demand reservations cannot be relisted.

        Requires Idempotency-Key. Publication to the Market and Spot follows within moments of the response.
      parameters:
        - name: reservationId
          in: path
          required: true
          description: A reservation your organization holds.
          schema: *id004
          example: 11111111-1111-4111-8111-111111111111
        - *id012
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CapacityRelistRequest"
            example:
              startsAt: "2026-10-20T00:00:00Z"
              endsAt: "2026-11-03T00:00:00Z"
              nodeCount: 2
              nodeIds:
                - 33333333-3333-4333-8333-333333333333
                - 12121212-1212-4212-8212-121212121212
              terms:
                - type: reserved
                  minDurationHours: 24
                  maxDurationHours: 336
                  pricePerGpuHourMicroUsd: 4000000
                  downPaymentCents:
                  renewable: false
      responses:
        "201":
          description: Listing created and available.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CapacityRelistCreated"
              example:
                listing:
                  capacityListingId: eeeeeeee-eeee-4eee-8eee-eeeeeeeeeeee
                  listingNumber: 1188
                  parentReservationId: 11111111-1111-4111-8111-111111111111
                  kind: secondary
                  state: available
                  gpuSlug: nvidia_h100_sxm
                  gpusPerNode: 8
                  capacityNodeCount: 2
                  facilityId: cccccccc-cccc-4ccc-8ccc-cccccccccccc
                  provisioningType: virtual-machine
                  availableAt: "2026-10-20T00:00:00Z"
                  availableUntil: "2026-11-03T00:00:00Z"
                  daysToRfs:
                  cpu: 2x 56-core x86
                  ram: 2 TB
                  storage: 8x 3.84 TB NVMe
                  fabricType: InfiniBand 3.2 Tbps
                  internet: 10 Gbps
                  createdAt: "2026-10-14T18:30:00Z"
                  updatedAt: "2026-10-14T18:30:00Z"
                terms:
                  - listingTermsId: 13131313-1313-4313-8313-131313131313
                    capacityListingId: eeeeeeee-eeee-4eee-8eee-eeeeeeeeeeee
                    type: reserved
                    renewable: false
                    minDurationHours: 24
                    maxDurationHours: 336
                    downPaymentCents:
                    pricePerGpuHourMicroUsd: 4000000
                    createdAt: "2026-10-14T18:30:00Z"
        "400": *id011
        "403":
          description: The reservation does not belong to your organization.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: forbidden
                  message: Your account cannot make this change.
        "404":
          description: The reservation does not exist.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: reservation_not_found
                  message: Ornn could not find that reservation.
        "409":
          description: The nodes are not available to relist for these dates, delivery is pending, or the key was reused. Codes include relist_delivery_pending, relist_nodes_unavailable, invalid_relist_nodes, and idempotency_conflict.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: relist_nodes_unavailable
                  message: A selected node is already listed or reserved during these dates. Choose other nodes or dates.
        "422":
          description: Invalid window, node count, terms, or missing Idempotency-Key. Codes include invalid_relist, invalid_time_precision, invalid_terms, invalid_hourly_terms, invalid_duration, invalid_spot_listing, and invalid_idempotency_key.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: invalid_relist
                  message: The relist must fit within the parent reservation's dates and node count.
        "401":
          $ref: "#/components/responses/Unauthorized"
        default:
          $ref: "#/components/responses/Error"
  /v1/orchestrator/drives:
    get:
      summary: List volumes
      operationId: platformListVolumes
      tags:
        - Storage
      description: >-
        Lists one page of volumes owned by the API key's creator in its organization, most recently updated
        first. Follow next_cursor with the same filters until it is null. A cursor is tied to its filters; change
        a filter and start again from the first page. With reservation_id, the endpoint instead returns the volume
        attached to that reservation and its attachment, and accepts no other query parameter.
      parameters:
        - name: limit
          in: query
          required: false
          description: Page size from 1 to 100. Defaults to 15.
          schema:
            type: integer
            minimum: 1
            maximum: 100
          example: 25
        - name: cursor
          in: query
          required: false
          description: Opaque next_cursor from the previous page, sent with the same filters.
          schema:
            type: string
        - name: q
          in: query
          required: false
          description: Case-insensitive text matched against volume name, ID, and region. At most 256 characters.
          schema:
            type: string
            maxLength: 256
        - name: deployment
          in: query
          required: false
          description: Filter by whether the volume is mounted on a machine.
          schema:
            type: string
            enum:
              - all
              - deployed
              - available
            default: all
        - name: reservation_id
          in: query
          required: false
          description: >-
            An owned reservation. Returns that reservation's attached volume and attachment instead of a page.
            Cannot be combined with other query parameters.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: >-
            A page of volumes, or the reservation's attachment when reservation_id is set. Ignore fields you do
            not recognize.
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: "#/components/schemas/PlatformVolumePage"
                  - $ref: "#/components/schemas/PlatformReservationVolume"
              example:
                drives:
                  - id: 77777777-7777-4777-8777-777777777777
                    source_drive_id:
                    name: training-data
                    status: active
                    size_bytes: 1073741824
                    file_count: 42
                    file_tree_status: ready
                    content_epoch: 7
                    source:
                      status: ready
                      region: us-central1
                      read_only: false
                      github_repositories: []
                    file_capabilities:
                      state: ready
                      can_upload: true
                      message:
                    active_mount:
                    active_mounts: []
                    created_at: "2026-10-01T12:00:00Z"
                    updated_at: "2026-10-14T12:00:00Z"
                next_cursor:
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "422":
          description: Invalid page size, filter, or cursor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: storage_page_invalid
                  message: Choose a page size from 1 to 100 and valid storage filters, or restart from the first page.
        default:
          $ref: "#/components/responses/Error"
    post:
      summary: Create a volume
      operationId: platformCreateVolume
      tags:
        - Storage
      description: >-
        Creates an empty volume, or duplicates one of your volumes when source_drive_id is set. A duplicate copies
        files in the background; its size is reported when the copy finishes. Creating a volume does not attach it
        to compute. Object storage is billed for the data stored. This request is not idempotent; after a timeout,
        list volumes by name before retrying.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PlatformCreateVolume"
            example:
              name: training-data
      responses:
        "201":
          description: The new volume.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlatformVolume"
              example:
                id: 77777777-7777-4777-8777-777777777777
                source_drive_id:
                name: training-data
                status: active
                size_bytes: 0
                file_count: 0
                file_tree_status: ready
                content_epoch: 0
                source:
                  status: ready
                  region: us-central1
                  read_only: false
                  github_repositories: []
                file_capabilities:
                  state: ready
                  can_upload: true
                  message:
                active_mount:
                active_mounts: []
                created_at: "2026-10-14T12:00:00Z"
                updated_at: "2026-10-14T12:00:00Z"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          description: The source volume is not one of your volumes.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "409":
          description: The source volume is busy, for example while a repository sync or upload is in progress.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "422":
          description: Missing name or unsupported options.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: storage_drive_name_required
                  message: Give the volume a name.
        default:
          $ref: "#/components/responses/Error"
  /v1/orchestrator/drives/{drive_id}:
    parameters:
      - $ref: "#/components/parameters/PlatformDriveId"
    get:
      summary: Get a volume
      operationId: platformGetVolume
      tags:
        - Storage
      description: >-
        Returns one of your volumes with its size, recent files, repository pulls, and active mounts. Set
        refresh=true to re-measure the volume's size from its stored files before returning it.
      parameters:
        - name: refresh
          in: query
          required: false
          description: Re-measure size_bytes from the stored files. Slower than a plain read.
          schema:
            type: boolean
            default: false
      responses:
        "200":
          description: The volume.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlatformVolume"
              example:
                id: 77777777-7777-4777-8777-777777777777
                source_drive_id:
                name: training-data
                status: active
                size_bytes: 1073741824
                file_count: 42
                file_tree_status: ready
                content_epoch: 7
                source:
                  status: ready
                  region: us-central1
                  read_only: false
                  github_repositories: []
                file_capabilities:
                  state: ready
                  can_upload: true
                  message:
                active_mount:
                  id: ffffffff-ffff-4fff-8fff-ffffffffffff
                  drive_id: 77777777-7777-4777-8777-777777777777
                  reservation_id: 11111111-1111-4111-8111-111111111111
                  node_id: 33333333-3333-4333-8333-333333333333
                  mount_path: /home/tenant/volume
                  access: read-write
                  state: mounted
                  error:
                  created_at: "2026-10-14T12:05:00Z"
                  updated_at: "2026-10-14T12:07:00Z"
                active_mounts:
                  - id: ffffffff-ffff-4fff-8fff-ffffffffffff
                    drive_id: 77777777-7777-4777-8777-777777777777
                    reservation_id: 11111111-1111-4111-8111-111111111111
                    node_id: 33333333-3333-4333-8333-333333333333
                    mount_path: /home/tenant/volume
                    access: read-write
                    state: mounted
                    error:
                    created_at: "2026-10-14T12:05:00Z"
                    updated_at: "2026-10-14T12:07:00Z"
                created_at: "2026-10-01T12:00:00Z"
                updated_at: "2026-10-14T12:07:00Z"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/PlatformVolumeNotFound"
        default:
          $ref: "#/components/responses/Error"
    patch:
      summary: Attach or detach a volume
      operationId: platformUpdateVolumeAttachment
      tags:
        - Storage
      description: >-
        Attaches the volume to an owned reservation with deployed true, or detaches it with attached false.
        Ornn mounts an attached volume on every machine in the reservation; there is no per-machine selection.
        Every machine in the reservation must be online. A reservation holds one volume at a time, and a volume
        attaches to one reservation at a time. To change the access mode or mount path, detach first. Both
        operations are asynchronous: poll List volumes with reservation_id. Detaching keeps the volume and its
        data and does not change reservation billing.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PlatformVolumeAttachmentChange"
            examples:
              attach:
                summary: Attach read-write
                value:
                  reservation_id: 11111111-1111-4111-8111-111111111111
                  deployed: true
                  access_mode: read-write
                  mount_path: /home/tenant/volume
              detach:
                summary: Detach
                value:
                  reservation_id: 11111111-1111-4111-8111-111111111111
                  attached: false
      responses:
        "200":
          description: >-
            The request was accepted. attachment.state is pre_positioning after an attach and detaching after a
            detach. attachment is null when there was nothing to detach.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlatformVolumeAttachmentEnvelope"
              example:
                attachment:
                  id: aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa
                  reservation_id: 11111111-1111-4111-8111-111111111111
                  drive_id: 77777777-7777-4777-8777-777777777777
                  mount_path: /home/tenant/volume
                  mount_mode: fuse
                  access_mode: read-write
                  state: pre_positioning
                  error:
                  created_at: "2026-10-14T12:05:00Z"
                  placement:
                    state: pre_positioning
                    target_region: us-central1
                    error:
                    ready_at:
                    transfer:
                      state: queued
                      objects_total:
                      objects_copied:
                      bytes_total:
                      bytes_copied:
                      error:
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          description: The volume or reservation is not visible to your organization.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "409":
          description: >-
            The change conflicts with current state, such as an offline machine, a volume attached elsewhere, a
            detach in progress, or a change that requires detaching first.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: storage_reservation_node_offline
                  message: A node in this reservation is offline. Bring every node online.
        "422":
          description: The reservation's location cannot hold storage yet.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: storage_location_unavailable
                  message: >-
                    Ornn cannot place storage for this reservation location yet. Ask Ornn Support to set a storage
                    region for the facility.
        default:
          $ref: "#/components/responses/Error"
    delete:
      summary: Delete a volume
      operationId: platformDeleteVolume
      tags:
        - Storage
      description: >-
        Permanently deletes the volume and every file in it. This cannot be undone. A volume attached to a
        reservation returns 409 unless detach=true, which detaches it and deletes it in the background (202).
        Deletion is refused while a repository sync, upload, or file deletion is in progress.
      parameters:
        - name: detach
          in: query
          required: false
          description: Detach the volume from its reservation first, then delete it in the background.
          schema:
            type: boolean
            default: false
      responses:
        "202":
          description: Detach and deletion accepted. Poll Get a volume until it returns 404.
        "204":
          description: The volume was deleted.
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/PlatformVolumeNotFound"
        "409":
          description: The volume is attached to a reservation, or another operation is in progress.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: storage_drive_attached_to_reservation
                  message: This volume is attached to a reservation. Detach it first.
        default:
          $ref: "#/components/responses/Error"
  /v1/orchestrator/drives/{drive_id}/contents:
    parameters:
      - $ref: "#/components/parameters/PlatformDriveId"
    get:
      summary: List volume files
      operationId: platformListVolumeFiles
      tags:
        - Storage
      description: >-
        Lists one folder level of the volume, folders first, 200 entries per page. Set recursive=true or search to
        list matching files at any depth instead. Pass next_cursor unchanged with the same path and search to read
        the next page.
      parameters:
        - name: path
          in: query
          required: false
          description: Folder to list, relative to the volume root. Omit for the root.
          schema:
            type: string
          example: results/run1
        - name: search
          in: query
          required: false
          description: Case-insensitive text matched against file paths at any depth.
          schema:
            type: string
        - name: recursive
          in: query
          required: false
          description: List every file below path instead of one folder level.
          schema:
            type: boolean
            default: false
        - name: cursor
          in: query
          required: false
          description: next_cursor from the previous page.
          schema:
            type: string
      responses:
        "200":
          description: One page of entries.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlatformVolumeContents"
              example:
                path: results
                entries:
                  - path: results/run1
                    name: run1
                    kind: directory
                  - path: results/summary.csv
                    name: summary.csv
                    kind: file
                    size_bytes: 2048
                    updated_at: "2026-10-14T12:00:00Z"
                total: 2
                next_cursor:
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/PlatformVolumeNotFound"
        default:
          $ref: "#/components/responses/Error"
    delete:
      summary: Delete all volume files
      operationId: platformClearVolume
      tags:
        - Storage
      description: >-
        Permanently deletes every file and folder in the volume and keeps the empty volume. Refused while the
        volume is mounted on a machine; detach it first.
      responses:
        "200":
          description: The files were deleted.
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                  contents:
                    type: object
                    properties:
                      deleted:
                        type: integer
                        description: Number of stored objects deleted.
              example:
                ok: true
                contents:
                  deleted: 42
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/PlatformVolumeNotFound"
        "409":
          $ref: "#/components/responses/PlatformVolumeMounted"
        default:
          $ref: "#/components/responses/Error"
  /v1/orchestrator/drives/{drive_id}/files/{file_path}:
    parameters:
      - $ref: "#/components/parameters/PlatformDriveId"
      - $ref: "#/components/parameters/PlatformFilePath"
    get:
      summary: Download a file
      operationId: platformDownloadVolumeFile
      tags:
        - Storage
      description: Downloads one file from the volume as an attachment.
      responses:
        "200":
          description: The file bytes.
          content:
            application/octet-stream:
              schema:
                type: string
                format: binary
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          description: The volume or file does not exist.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        default:
          $ref: "#/components/responses/Error"
    put:
      summary: Upload a small file
      operationId: platformUploadVolumeFile
      tags:
        - Storage
      description: >-
        Writes the request body to this path, replacing any existing file. The API gateway rejects bodies larger
        than 32 MiB; use a staged upload for larger files. Refused while the volume is mounted on a machine.
      requestBody:
        required: true
        content:
          application/octet-stream:
            schema:
              type: string
              format: binary
      responses:
        "200":
          description: The file was written.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlatformFileWrite"
              example:
                ok: true
                file:
                  path: config/train.yaml
                  size_bytes: 512
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/PlatformVolumeNotFound"
        "409":
          $ref: "#/components/responses/PlatformVolumeMounted"
        "413":
          description: The file is larger than the limit for this operation.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        default:
          $ref: "#/components/responses/Error"
    delete:
      summary: Delete a file
      operationId: platformDeleteVolumeFile
      tags:
        - Storage
      description: Permanently deletes one file. Refused while the volume is mounted on a machine.
      responses:
        "200":
          description: The file was deleted.
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                  file:
                    type: object
                    properties:
                      path:
                        type: string
              example:
                ok: true
                file:
                  path: results/summary.csv
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          description: The volume or file does not exist.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: storage_file_not_found
                  message: That file is not in this volume.
        "409":
          $ref: "#/components/responses/PlatformVolumeMounted"
        default:
          $ref: "#/components/responses/Error"
  /v1/orchestrator/drives/{drive_id}/folders/{folder_path}:
    parameters:
      - $ref: "#/components/parameters/PlatformDriveId"
      - name: folder_path
        in: path
        required: true
        description: Folder path relative to the volume root. Keep / between folder names and encode each name.
        schema:
          type: string
        example: results/run1
    delete:
      summary: Delete a folder
      operationId: platformDeleteVolumeFolder
      tags:
        - Storage
      description: >-
        Permanently deletes a folder and every file below it. Refused while the volume is mounted on a machine.
      responses:
        "200":
          description: The folder was deleted.
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                  folder:
                    type: object
                    properties:
                      path:
                        type: string
                      deleted:
                        type: integer
              example:
                ok: true
                folder:
                  path: results/run1
                  deleted: 12
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/PlatformVolumeNotFound"
        "409":
          $ref: "#/components/responses/PlatformVolumeMounted"
        default:
          $ref: "#/components/responses/Error"
  /v1/orchestrator/drives/{drive_id}/archive:
    parameters:
      - $ref: "#/components/parameters/PlatformDriveId"
    get:
      summary: Download a volume as a ZIP archive
      operationId: platformDownloadVolumeArchive
      tags:
        - Storage
      description: >-
        Streams the volume's files as a ZIP archive. Large volumes can exceed the archive limit; download
        individual files instead.
      responses:
        "200":
          description: A ZIP archive of the volume.
          content:
            application/zip:
              schema:
                type: string
                format: binary
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/PlatformVolumeNotFound"
        default:
          $ref: "#/components/responses/Error"
  /v1/orchestrator/drives/{drive_id}/uploads:
    parameters:
      - $ref: "#/components/parameters/PlatformDriveId"
    post:
      summary: Start a staged upload
      operationId: platformStartVolumeUpload
      tags:
        - Storage
      description: >-
        Reserves an upload of one file and returns a short-lived URL. Send the bytes to upload_url with the
        returned method and headers, then complete the upload. The session expires after about 15 minutes. Send
        an Idempotency-Key header to retry safely; a retry with the same key and unchanged input returns the same
        session, or state complete if it already finished. The file appears in the volume only after completion.
      parameters:
        - name: Idempotency-Key
          in: header
          required: false
          description: Caller-chosen key of up to 128 characters, unique per upload.
          schema:
            type: string
            maxLength: 128
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PlatformStartUpload"
            example:
              path: datasets/train.parquet
              size_bytes: 524288000
              content_type: application/octet-stream
      responses:
        "200":
          description: The upload session, or state complete for a finished retry.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlatformUploadSession"
              example:
                upload_id: bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb
                path: datasets/train.parquet
                method: PUT
                upload_url: https://storage.example.com/upload/bbbbbbbb?signature=example
                headers:
                  Content-Type: application/octet-stream
                expires_in_seconds: 900
                max_size_bytes: 10737418240
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/PlatformVolumeNotFound"
        "409":
          description: The idempotency key was used for different input, the session expired, or the volume is busy.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: storage_upload_idempotency_conflict
                  message: This upload key was already used for different input. Use a new key for a different upload.
        "413":
          description: The declared size exceeds the upload limit.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        default:
          $ref: "#/components/responses/Error"
  /v1/orchestrator/drives/{drive_id}/uploads/{upload_id}:
    parameters:
      - $ref: "#/components/parameters/PlatformDriveId"
      - name: upload_id
        in: path
        required: true
        description: upload_id returned by Start a staged upload.
        schema:
          type: string
          format: uuid
        example: bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb
    post:
      summary: Complete a staged upload
      operationId: platformCompleteVolumeUpload
      tags:
        - Storage
      description: >-
        Moves the uploaded bytes into the volume at the reserved path, replacing any existing file there. Safe to
        repeat; a completed upload returns state complete again. The request needs a JSON body; send `{}`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
            example: {}
      responses:
        "200":
          description: The file is in the volume.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlatformUploadResult"
              example:
                ok: true
                upload_id: bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb
                path: datasets/train.parquet
                state: complete
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          description: The upload session expired or does not exist. Start the upload again.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: storage_upload_not_found
                  message: That upload has expired. Start it again.
        "409":
          description: The bytes were not uploaded, or the session expired before completion.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        default:
          $ref: "#/components/responses/Error"
    delete:
      summary: Cancel a staged upload
      operationId: platformCancelVolumeUpload
      tags:
        - Storage
      description: >-
        Cancels an incomplete upload and discards any uploaded bytes. A completed upload is not undone; the
        response reports state complete.
      responses:
        "200":
          description: The upload was cancelled, or it had already completed.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlatformUploadResult"
              example:
                ok: true
                upload_id: bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb
                state: cancelled
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          description: The upload does not exist.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        default:
          $ref: "#/components/responses/Error"
  /v1/orchestrator/github/connections:
    get:
      summary: List GitHub connections
      operationId: platformListGithubConnections
      tags:
        - Storage
      description: >-
        Lists the GitHub accounts the API key's creator connected in its organization. Connecting a GitHub account
        requires the browser flow in Account settings; this API cannot start it.
      responses:
        "200":
          description: Connected GitHub accounts.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlatformGithubConnections"
              example:
                configured: true
                connections:
                  - id: dddddddd-dddd-4ddd-8ddd-dddddddddddd
                    state: connected
                    account_login: example-org
                    account_type: Organization
                    connected_at: "2026-10-01T12:00:00Z"
                    installation_url: https://github.com/organizations/example-org/settings/installations/1
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        default:
          $ref: "#/components/responses/Error"
  /v1/orchestrator/github/connections/{connection_id}:
    parameters:
      - $ref: "#/components/parameters/PlatformGithubConnectionId"
    delete:
      summary: Disconnect a GitHub account
      operationId: platformDisconnectGithub
      tags:
        - Storage
      description: >-
        Removes the GitHub account from Ornn. The app stays installed on GitHub. Files already pulled stay in
        their volumes, but repositories pulled through this connection can no longer sync.
      responses:
        "204":
          description: The connection was removed.
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/PlatformGithubConnectionNotFound"
        default:
          $ref: "#/components/responses/Error"
  /v1/orchestrator/github/connections/{connection_id}/repositories:
    parameters:
      - $ref: "#/components/parameters/PlatformGithubConnectionId"
    get:
      summary: List GitHub repositories
      operationId: platformListGithubRepositories
      tags:
        - Storage
      description: Lists every repository the connected installation lets Ornn read.
      responses:
        "200":
          description: Readable repositories.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlatformGithubRepositories"
              example:
                repositories:
                  - id: 123456789
                    full_name: example-org/training
                    private: true
                    default_branch: main
                    archived: false
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/PlatformGithubConnectionNotFound"
        "409":
          description: Ornn no longer has access to this installation. Connect GitHub again in Account settings.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: GitHub is rate limiting Ornn. Retry later.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        default:
          $ref: "#/components/responses/Error"
  /v1/orchestrator/github/connections/{connection_id}/repositories/{repository_id}/branches:
    parameters:
      - $ref: "#/components/parameters/PlatformGithubConnectionId"
      - name: repository_id
        in: path
        required: true
        description: GitHub repository id from List GitHub repositories.
        schema:
          type: integer
          format: int64
        example: 123456789
    get:
      summary: List repository branches
      operationId: platformListGithubBranches
      tags:
        - Storage
      description: Lists one page of up to 100 branches. Request the next page while has_more is true.
      parameters:
        - name: full_name
          in: query
          required: true
          description: Repository owner and name, as returned by List GitHub repositories.
          schema:
            type: string
          example: example-org/training
        - name: page
          in: query
          required: false
          description: Page number, starting at 1.
          schema:
            type: integer
            minimum: 1
            default: 1
      responses:
        "200":
          description: One page of branches.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlatformGithubBranches"
              example:
                branches:
                  - name: main
                    commit_sha: 0123456789abcdef0123456789abcdef01234567
                has_more: false
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          description: The connection or repository is not readable.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        default:
          $ref: "#/components/responses/Error"
  /v1/orchestrator/drives/{drive_id}/github:
    parameters:
      - $ref: "#/components/parameters/PlatformDriveId"
    post:
      summary: Pull a repository into a volume
      operationId: platformPullGithubRepository
      tags:
        - Storage
      description: >-
        Queues a pull of one branch into a new folder on the volume and returns the volume. The pull runs in the
        background. Poll Get a volume and read source.github_repositories for the folder until sync.state is ready
        or failed. The folder must not already contain files. A volume runs one pull or sync at a time.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PlatformPullRepository"
            example:
              connection_id: dddddddd-dddd-4ddd-8ddd-dddddddddddd
              repository_id: 123456789
              full_name: example-org/training
              branch: main
              folder: training
      responses:
        "200":
          description: The pull is queued. The volume is returned with the new repository entry.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlatformVolume"
              example:
                id: 77777777-7777-4777-8777-777777777777
                source_drive_id:
                name: training-data
                status: active
                size_bytes: 0
                file_count: 0
                file_tree_status: ready
                content_epoch: 0
                source:
                  status: ready
                  region: us-central1
                  read_only: false
                  github_repositories:
                    - folder: training
                      full_name: example-org/training
                      branch: main
                      connection_id: dddddddd-dddd-4ddd-8ddd-dddddddddddd
                      sync:
                        state: queued
                        requested_at: "2026-10-14T12:00:00Z"
                file_capabilities:
                  state: ready
                  can_upload: true
                  message:
                active_mount:
                active_mounts: []
                created_at: "2026-10-01T12:00:00Z"
                updated_at: "2026-10-14T12:00:00Z"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          description: The volume or GitHub connection was not found.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: storage_github_connection_not_found
                  message: Ornn has no GitHub account linked for this. Connect GitHub in Account settings, then try again.
        "409":
          description: The folder already has files, or another sync, upload, or deletion is running.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: storage_github_folder_taken
                  message: This volume already has a folder with that name. Choose another name.
        "422":
          description: Invalid repository name, branch, or folder.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        default:
          $ref: "#/components/responses/Error"
  /v1/orchestrator/drives/{drive_id}/github/{folder}/sync:
    parameters:
      - $ref: "#/components/parameters/PlatformDriveId"
      - name: folder
        in: path
        required: true
        description: The repository folder on the volume.
        schema:
          type: string
        example: training
    post:
      summary: Sync a repository
      operationId: platformSyncGithubRepository
      tags:
        - Storage
      description: >-
        Queues a pull of the latest commit of the folder's branch into the same folder and returns the volume.
        A sync can replace or delete repository files while the volume is mounted. Poll Get a volume until the
        folder's sync.state is ready or failed.
      responses:
        "200":
          description: The sync is queued.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlatformVolume"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          description: The volume has no repository in that folder.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "409":
          description: A sync, upload, or deletion is already running on this volume.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: storage_github_sync_in_progress
                  message: A repository on this volume is syncing. Wait for it to finish, then try again.
        default:
          $ref: "#/components/responses/Error"
  /v1/orchestrator/networks:
    get:
      summary: List private networks
      operationId: platformListNetworks
      tags:
        - Networks
      description: Lists the organization's private networks, oldest first. Deleted networks are omitted.
      responses:
        "200":
          description: The organization's networks.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlatformNetworkList"
              example:
                networks:
                  - id: 88888888-8888-4888-8888-888888888888
                    name: research
                    cidr: 10.20.0.0/16
                    status: active
                    endpoint: 203.0.113.10
                    public_key: exampleGatewayPublicKeyBase64AAAAAAAAAAAAA=
                    attached_reservation_count: 2
                    gateway:
                      status: active
                      endpoint: 203.0.113.10
                      public_key: exampleGatewayPublicKeyBase64AAAAAAAAAAAAA=
                    compartments: []
                    created_at: "2026-10-01T12:00:00Z"
                    updated_at: "2026-10-01T12:10:00Z"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        default:
          $ref: "#/components/responses/Error"
    post:
      summary: Create a private network
      operationId: platformCreateNetwork
      tags:
        - Networks
      description: >-
        Creates a named organization network and starts provisioning its gateway. The network is usable when
        status is active; poll Get a private network. Omit cidr to have Ornn choose a free /16 range. Names are
        unique within the organization, ignoring case. This request is not idempotent; after a timeout, list
        networks by name before retrying.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PlatformCreateNetwork"
            example:
              name: research
              cidr: 10.20.0.0/16
      responses:
        "201":
          description: Network created; provisioning continues in the background.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlatformNetworkEnvelope"
              example:
                network:
                  id: 88888888-8888-4888-8888-888888888888
                  name: research
                  cidr: 10.20.0.0/16
                  status: pending
                  endpoint:
                  public_key:
                  attached_reservation_count: 0
                  gateway:
                    status: pending
                    endpoint:
                    public_key:
                  compartments:
                    - network_id: 88888888-8888-4888-8888-888888888888
                      compartment_id: provision_key
                      status: pending
                      error:
                  created_at: "2026-10-14T12:00:00Z"
                  updated_at: "2026-10-14T12:00:00Z"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "409":
          description: The name or CIDR range is already in use, or no automatic range is free.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: network_cidr_overlap
                  message: That CIDR range overlaps an existing or reserved network.
        "422":
          description: Missing name, or a CIDR that is invalid, not private, or outside /16 to /24.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: invalid_network_cidr
                  message: Enter a valid CIDR block, like 10.0.0.0/16.
        default:
          $ref: "#/components/responses/Error"
  /v1/orchestrator/networks/{network_id}:
    parameters:
      - $ref: "#/components/parameters/PlatformNetworkId"
    get:
      summary: Get a private network
      operationId: platformGetNetwork
      tags:
        - Networks
      description: >-
        Returns one network. Poll this after creation until status is active, or after deletion until it returns
        404. compartments lists the provisioning or teardown steps and their status.
      responses:
        "200":
          description: The network.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlatformNetwork"
              example:
                id: 88888888-8888-4888-8888-888888888888
                name: research
                cidr: 10.20.0.0/16
                status: active
                endpoint: 203.0.113.10
                public_key: exampleGatewayPublicKeyBase64AAAAAAAAAAAAA=
                attached_reservation_count: 0
                gateway:
                  status: active
                  endpoint: 203.0.113.10
                  public_key: exampleGatewayPublicKeyBase64AAAAAAAAAAAAA=
                compartments:
                  - network_id: 88888888-8888-4888-8888-888888888888
                    compartment_id: provision_key
                    status: completed
                    error:
                created_at: "2026-10-14T12:00:00Z"
                updated_at: "2026-10-14T12:10:00Z"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/PlatformNetworkNotFound"
        default:
          $ref: "#/components/responses/Error"
    patch:
      summary: Rename a private network
      operationId: platformRenameNetwork
      tags:
        - Networks
      description: Changes the network's name. The CIDR range cannot change.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PlatformRenameNetwork"
            example:
              name: research-east
      responses:
        "200":
          description: The renamed network.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlatformNetworkEnvelope"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/PlatformNetworkNotFound"
        "409":
          description: Another network already has that name.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: network_name_conflict
                  message: A network with that name already exists.
        default:
          $ref: "#/components/responses/Error"
    delete:
      summary: Delete a private network
      operationId: platformDeleteNetwork
      tags:
        - Networks
      description: >-
        Deletes a network that has no connected peers and tears down its gateway in the background. Remove
        WireGuard configs and stop machines and clusters using the network first. Poll Get a private network
        until it returns 404.
      responses:
        "204":
          description: Deletion accepted.
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/PlatformNetworkNotFound"
        "409":
          description: Peers are still connected to the network.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: network_in_use
                  message: This network still has machines connected. Disconnect them before you delete it.
        default:
          $ref: "#/components/responses/Error"
  /v1/orchestrator/networks/shared/peers:
    get:
      summary: List default-network WireGuard configs
      operationId: platformListDefaultNetworkPeers
      tags:
        - Networks
      description: >-
        Lists the WireGuard configs the API key's creator registered on the organization's default private
        network, with a ready-to-fill config for each.
      responses:
        "200":
          description: Your WireGuard configs on the default network.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlatformWireGuardPeerList"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        default:
          $ref: "#/components/responses/Error"
    post:
      summary: Add a default-network WireGuard config
      operationId: platformCreateDefaultNetworkPeer
      tags:
        - Networks
      description: >-
        Registers your WireGuard public key on the organization's default private network and returns a config
        with a placeholder for your private key. Generate the key pair locally and never send the private key.
        Retrying with the same public key returns the existing config.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PlatformCreateWireGuardPeer"
            example:
              public_key: exampleClientPublicKeyBase64AAAAAAAAAAAAAA=
      responses:
        "201":
          description: The config. peer.status is active once the gateway applied it.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlatformWireGuardPeerConfig"
              example:
                peer:
                  id: 99999999-9999-4999-8999-999999999999
                  kind: user
                  public_key: exampleClientPublicKeyBase64AAAAAAAAAAAAAA=
                  assigned_ip: 100.96.0.10
                  status: active
                  created_at: "2026-10-14T12:00:00Z"
                  revoked_at:
                network:
                  id:
                  cidr: 100.96.0.0/16
                  gateway_ip: 100.96.0.1
                  status: active
                  gateway:
                    public_endpoint: 203.0.113.20
                    public_key: exampleGatewayPublicKeyBase64BBBBBBBBBBBBB=
                    udp_port: 51820
                    status: configured
                config: |
                  [Interface]
                  PrivateKey = <your-wireguard-private-key>
                  Address = 100.96.0.10/32
                  MTU = 1200
                  DNS = 1.1.1.1

                  [Peer]
                  PublicKey = exampleGatewayPublicKeyBase64BBBBBBBBBBBBB=
                  Endpoint = 203.0.113.20:51820
                  AllowedIPs = 100.96.0.0/16
                  PersistentKeepalive = 25
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          description: Private networking is not available for this organization yet.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "409":
          description: The key is used by someone else, or you reached the config limit.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: wireguard_public_key_already_used
                  message: >-
                    This WireGuard public key is already in use on this VPN gateway. Generate a new keypair, or
                    remove the existing config first.
        "422":
          description: The public key is not a valid WireGuard key.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        default:
          $ref: "#/components/responses/Error"
  /v1/orchestrator/networks/shared/peers/{peer_id}:
    parameters:
      - $ref: "#/components/parameters/PlatformPeerId"
    delete:
      summary: Remove a default-network WireGuard config
      operationId: platformDeleteDefaultNetworkPeer
      tags:
        - Networks
      description: Revokes one of your WireGuard configs on the default network. Private access through it ends.
      responses:
        "200":
          description: The revoked config.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlatformWireGuardPeer"
              example:
                id: 99999999-9999-4999-8999-999999999999
                kind: user
                public_key: exampleClientPublicKeyBase64AAAAAAAAAAAAAA=
                assigned_ip: 100.96.0.10
                status: revoked
                created_at: "2026-10-14T12:00:00Z"
                revoked_at: "2026-10-14T13:00:00Z"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          description: The config belongs to another user.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          $ref: "#/components/responses/PlatformPeerNotFound"
        "502":
          description: The gateway did not confirm the revocation. Retry.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        default:
          $ref: "#/components/responses/Error"
  /v1/orchestrator/networks/{network_id}/peers:
    parameters:
      - $ref: "#/components/parameters/PlatformNetworkId"
    get:
      summary: List network WireGuard configs
      operationId: platformListNetworkPeers
      tags:
        - Networks
      description: Lists the WireGuard configs the API key's creator registered on a network you created.
      responses:
        "200":
          description: Your WireGuard configs on the network.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlatformWireGuardPeerList"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/PlatformNetworkNotFound"
        default:
          $ref: "#/components/responses/Error"
    post:
      summary: Add a network WireGuard config
      operationId: platformCreateNetworkPeer
      tags:
        - Networks
      description: >-
        Registers your WireGuard public key on a network you created and returns a config with a placeholder for
        your private key. The network must be active. Retrying with the same public key returns the existing
        config.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PlatformCreateWireGuardPeer"
            example:
              public_key: exampleClientPublicKeyBase64AAAAAAAAAAAAAA=
      responses:
        "201":
          description: The config.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlatformWireGuardPeerConfig"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          description: The network is not visible, or its gateway is not ready yet.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "409":
          description: The key is used by someone else, or you reached the config limit.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "422":
          description: The public key is not a valid WireGuard key.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: invalid_wireguard_public_key
                  message: That is not a valid WireGuard public key. Paste the 44 character key from your WireGuard client.
        default:
          $ref: "#/components/responses/Error"
  /v1/orchestrator/networks/{network_id}/peers/{peer_id}:
    parameters:
      - $ref: "#/components/parameters/PlatformNetworkId"
      - $ref: "#/components/parameters/PlatformPeerId"
    delete:
      summary: Remove a network WireGuard config
      operationId: platformDeleteNetworkPeer
      tags:
        - Networks
      description: Revokes one of your WireGuard configs on a network you created. Private access through it ends.
      responses:
        "200":
          description: The revoked config.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlatformWireGuardPeer"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          description: The config belongs to another user.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          $ref: "#/components/responses/PlatformPeerNotFound"
        "502":
          description: The gateway did not confirm the revocation. Retry.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        default:
          $ref: "#/components/responses/Error"
  /v1/observability/nodes/telemetry/latest:
    post:
      summary: Get latest telemetry for many machines
      operationId: platformBatchLatestTelemetry
      tags:
        - Monitoring
      description: >-
        Returns the most recent telemetry sample for up to 5,000 node and VM IDs combined. A machine with no sample
        inside your reservation window is omitted. If any ID is not visible to your organization, the whole request
        returns 404; split requests by reservation to isolate failures. Send large ID sets in parallel chunks
        rather than one at a time.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PlatformTelemetryBatchRequest"
            example:
              node_ids:
                - 33333333-3333-4333-8333-333333333333
              virtual_machine_ids:
                - 66666666-6666-4666-8666-666666666666
      responses:
        "200":
          description: Latest samples for the visible machines.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlatformTelemetryBatch"
              example:
                nodes:
                  - node_id: 33333333-3333-4333-8333-333333333333
                    observed_at: "2026-10-14T12:00:00Z"
                    samples: 1
                    values:
                      observed_at: "2026-10-14T12:00:00Z"
                      gpu_utilization: "87.5"
                      gpu_temperature: "64"
                      cpu_utilization: "22.1"
                      memory_utilization: "41.0"
                  - virtual_machine_id: 66666666-6666-4666-8666-666666666666
                    observed_at: "2026-10-14T12:00:05Z"
                    samples: 1
                    values:
                      observed_at: "2026-10-14T12:00:05Z"
                      gpu_utilization: "12.0"
        "400":
          description: An ID is not a UUID, or more than 5,000 IDs were sent.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlatformPlainError"
              example:
                error: too_many_node_ids
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/PlatformMachineNotVisible"
        default:
          $ref: "#/components/responses/Error"
  /v1/observability/nodes/{node_id}/telemetry/latest:
    parameters:
      - $ref: "#/components/parameters/PlatformNodeId"
    get:
      summary: Get latest node telemetry
      operationId: platformGetNodeLatestTelemetry
      tags:
        - Monitoring
      description: >-
        Returns the newest telemetry samples for a physical node in one of your reservations, newest first.
        Samples outside your reservation window are omitted, so an empty array means no sample in your window.
        Check observed_at for staleness; a stored sample is not proof the machine is reachable now.
      parameters:
        - $ref: "#/components/parameters/PlatformLatestLimit"
      responses:
        "200":
          description: Newest samples.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/PlatformTelemetryPoint"
              example:
                - observed_at: "2026-10-14T12:00:00Z"
                  samples: 1
                  values:
                    observed_at: "2026-10-14T12:00:00Z"
                    public_ip: 198.51.100.7
                    gpu_utilization: "87.5"
                    gpu_temperature: "64"
                    gpu_power_w: "612.4"
                    gpu_memory_utilization: "73.2"
                    cpu_utilization: "22.1"
                    memory_utilization: "41.0"
                    storage_utilization: "35.8"
                    ethernet_latency_ms: "0.4"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/PlatformMachineNotVisible"
        default:
          $ref: "#/components/responses/Error"
  /v1/observability/nodes/{node_id}/telemetry/query:
    parameters:
      - $ref: "#/components/parameters/PlatformNodeId"
    get:
      summary: Query node telemetry history
      operationId: platformQueryNodeTelemetry
      tags:
        - Monitoring
      description: >-
        Returns metric windows for a physical node over a time range, clamped to your reservation window. Ornn
        picks a resolution tier from the span and max_points. Set gpu_uuid to read one GPU's history over at most
        24 hours.
      parameters:
        - $ref: "#/components/parameters/PlatformRangeStart"
        - $ref: "#/components/parameters/PlatformRangeEnd"
        - $ref: "#/components/parameters/PlatformMaxPoints"
        - $ref: "#/components/parameters/PlatformMetrics"
        - name: gpu_uuid
          in: query
          required: false
          description: One GPU's UUID from the gpus reading. The range must span 24 hours or less.
          schema:
            type: string
          example: GPU-11111111-2222-3333-4444-555555555555
      responses:
        "200":
          description: Metric windows for the range.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlatformTelemetryWindows"
              example:
                tier: full
                windows:
                  - metric: gpu_utilization
                    start: "2026-10-14T11:00:00+00:00"
                    end: "2026-10-14T11:00:00+00:00"
                    mean: 87.5
                    median: 87.5
                    std: 0
                    min: 87.5
                    max: 87.5
        "400":
          $ref: "#/components/responses/PlatformTelemetryBadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/PlatformMachineNotVisible"
        default:
          $ref: "#/components/responses/Error"
  /v1/observability/virtual-machines/{virtual_machine_id}/telemetry/latest:
    parameters:
      - $ref: "#/components/parameters/PlatformVirtualMachineId"
    get:
      summary: Get latest VM telemetry
      operationId: platformGetVmLatestTelemetry
      tags:
        - Monitoring
      description: >-
        Returns the newest telemetry samples for a VM in one of your reservations, newest first, limited to your
        reservation window.
      parameters:
        - $ref: "#/components/parameters/PlatformLatestLimit"
      responses:
        "200":
          description: Newest samples.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/PlatformTelemetryPoint"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/PlatformMachineNotVisible"
        default:
          $ref: "#/components/responses/Error"
  /v1/observability/virtual-machines/{virtual_machine_id}/telemetry/query:
    parameters:
      - $ref: "#/components/parameters/PlatformVirtualMachineId"
    get:
      summary: Query VM telemetry history
      operationId: platformQueryVmTelemetry
      tags:
        - Monitoring
      description: >-
        Returns metric windows for a VM over a time range, clamped to your reservation window. Per-GPU history is
        not available for VMs.
      parameters:
        - $ref: "#/components/parameters/PlatformRangeStart"
        - $ref: "#/components/parameters/PlatformRangeEnd"
        - $ref: "#/components/parameters/PlatformMaxPoints"
        - $ref: "#/components/parameters/PlatformMetrics"
      responses:
        "200":
          description: Metric windows for the range.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlatformTelemetryWindows"
        "400":
          $ref: "#/components/responses/PlatformTelemetryBadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/PlatformMachineNotVisible"
        default:
          $ref: "#/components/responses/Error"
  /v1/observability/nodes/{node_id}/logs/{stream}/query:
    parameters:
      - $ref: "#/components/parameters/PlatformNodeId"
      - $ref: "#/components/parameters/PlatformLogStream"
    get:
      summary: Query node console logs
      operationId: platformQueryNodeLogs
      tags:
        - Monitoring
      description: >-
        Returns stored kernel or serial console lines for a physical node, clamped to your reservation window.
        Pages are newest first by default. Keep start and end fixed and pass next_cursor to read further back.
      parameters:
        - $ref: "#/components/parameters/PlatformRangeStart"
        - $ref: "#/components/parameters/PlatformRangeEnd"
        - $ref: "#/components/parameters/PlatformLogLimit"
        - $ref: "#/components/parameters/PlatformLogCursor"
        - $ref: "#/components/parameters/PlatformLogOrder"
      responses:
        "200":
          description: One page of log lines.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlatformLogPage"
              example:
                stream: kernel
                lines:
                  - date: "2026-10-14T11:58:12+00:00"
                    message: "NVRM: GPU at PCI:0000:18:00: GPU has fallen off the bus."
                    severity: error
                  - date: "2026-10-14T11:59:40+00:00"
                    message: "nvidia-persistenced: device 0000:18:00.0 recovered"
                    severity: info
                next_cursor:
        "400":
          description: Invalid stream, order, or time.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlatformPlainError"
              example:
                error: invalid_stream
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/PlatformMachineNotVisible"
        default:
          $ref: "#/components/responses/Error"
  /v1/observability/virtual-machines/{virtual_machine_id}/logs/{stream}/query:
    parameters:
      - $ref: "#/components/parameters/PlatformVirtualMachineId"
      - $ref: "#/components/parameters/PlatformLogStream"
    get:
      summary: Query VM console logs
      operationId: platformQueryVmLogs
      tags:
        - Monitoring
      description: >-
        Returns stored kernel or serial console lines for a VM, clamped to your reservation window. Pages are
        newest first by default.
      parameters:
        - $ref: "#/components/parameters/PlatformRangeStart"
        - $ref: "#/components/parameters/PlatformRangeEnd"
        - $ref: "#/components/parameters/PlatformLogLimit"
        - $ref: "#/components/parameters/PlatformLogCursor"
        - $ref: "#/components/parameters/PlatformLogOrder"
      responses:
        "200":
          description: One page of log lines.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlatformLogPage"
        "400":
          description: Invalid stream, order, or time.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlatformPlainError"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/PlatformMachineNotVisible"
        default:
          $ref: "#/components/responses/Error"
  /commerce/invoices:
    get:
      summary: List invoices
      operationId: platformListInvoices
      tags:
        - Billing
      description: >-
        Lists your organization's invoices, newest first. Follow nextCursor until it is absent. Amounts are integer
        cents in the invoice currency.
      parameters:
        - name: limit
          in: query
          required: false
          description: Page size. Defaults to 50; values above 100 return 100.
          schema:
            type: integer
            minimum: 1
            default: 50
        - name: cursor
          in: query
          required: false
          description: Opaque nextCursor from the previous page.
          schema:
            type: string
      responses:
        "200":
          description: One page of invoices.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlatformInvoicePage"
              example:
                invoices:
                  - invoiceId: cccccccc-cccc-4ccc-8ccc-cccccccccccc
                    reservationId: 11111111-1111-4111-8111-111111111111
                    type: recurring
                    amountCents: 4032000
                    currency: usd
                    status: open
                    dueDate: "2026-10-28"
                    hostedInvoiceUrl: https://invoice.example.com/i/example
                    invoicePdfUrl: https://invoice.example.com/i/example/pdf
                    servicePeriodStart: "2026-10-07"
                    servicePeriodEnd: "2026-10-14"
                    createdAt: "2026-10-14T00:05:00Z"
                    updatedAt: "2026-10-14T00:06:00Z"
        "400":
          $ref: "#/components/responses/PlatformPageBadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        default:
          $ref: "#/components/responses/Error"
  /commerce/invoices/{invoiceId}:
    parameters:
      - name: invoiceId
        in: path
        required: true
        description: Invoice ID.
        schema:
          type: string
          format: uuid
        example: cccccccc-cccc-4ccc-8ccc-cccccccccccc
    get:
      summary: Get an invoice
      operationId: platformGetInvoice
      tags:
        - Billing
      description: Returns one of your organization's invoices.
      responses:
        "200":
          description: The invoice.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlatformInvoice"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          description: The invoice is not visible to your organization.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlatformPlainError"
              example:
                error: invoice not found
        default:
          $ref: "#/components/responses/Error"
  /commerce/reservations/{reservationId}/invoices:
    parameters:
      - name: reservationId
        in: path
        required: true
        description: A reservation your organization holds.
        schema:
          type: string
          format: uuid
        example: 11111111-1111-4111-8111-111111111111
    get:
      summary: List a reservation's invoices
      operationId: platformListReservationInvoices
      tags:
        - Billing
      description: Lists every invoice for one reservation your organization holds, including the down payment.
      responses:
        "200":
          description: The reservation's invoices.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlatformReservationInvoiceList"
              example:
                invoices:
                  - invoiceId: cccccccc-cccc-4ccc-8ccc-cccccccccccc
                    type: down_payment
                    status: paid
                    amountCents: 1008000
                    currency: usd
                    dueDate: "2026-10-10T00:00:00Z"
                    paidAt: "2026-10-09T15:00:00Z"
                    servicePeriodStart:
                    servicePeriodEnd:
                    hostedInvoiceUrl: https://invoice.example.com/i/example
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          description: The reservation is not one your organization holds.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: reservation_not_found
                  message: Ornn could not find that reservation.
        default:
          $ref: "#/components/responses/Error"
  /commerce/billing/summary:
    get:
      summary: Get billing summary
      operationId: platformGetBillingSummary
      tags:
        - Billing
      description: >-
        Returns your open balance and paid totals for this month and last month, separately by currency. Draft and
        void invoices are excluded.
      responses:
        "200":
          description: Billing totals.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlatformBillingSummary"
              example:
                openBalance:
                  - currency: usd
                    amountCents: 4032000
                    count: 1
                earliestDueDate: "2026-10-28T00:00:00Z"
                paidThisMonth:
                  - currency: usd
                    amountCents: 1008000
                    count: 1
                paidLastMonth: []
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        default:
          $ref: "#/components/responses/Error"
  /commerce/billing/showback:
    get:
      summary: Get GPU usage cost
      operationId: platformGetUsageReport
      tags:
        - Billing
      description: >-
        Returns GPU-hours and usage cost per reservation for the part of each reservation inside the date range,
        using the reserved GPU count and agreed price. This is informational, not an invoice.
      parameters:
        - name: start
          in: query
          required: true
          description: First day of the range, YYYY-MM-DD.
          schema:
            type: string
            format: date
          example: "2026-10-01"
        - name: end
          in: query
          required: true
          description: Last day of the range, YYYY-MM-DD, on or after start.
          schema:
            type: string
            format: date
          example: "2026-10-14"
      responses:
        "200":
          description: Usage by reservation.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlatformUsageReport"
              example:
                windowStart: "2026-10-01"
                windowEnd: "2026-10-14"
                rows:
                  - reservationId: 11111111-1111-4111-8111-111111111111
                    status: active
                    gpuCount: 8
                    windowStart: "2026-10-07"
                    windowEnd: "2026-10-14"
                    gpuHours: 1344
                    costCents: 336000
                    sharePct: "100.00"
                totalGpuHours: 1344
                totalCostCents: 336000
        "400":
          description: start or end is missing, malformed, or out of order.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlatformPlainError"
              example:
                error: start must be YYYY-MM-DD
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        default:
          $ref: "#/components/responses/Error"
  /commerce/billing/contact:
    get:
      summary: Get billing contact
      operationId: platformGetBillingContact
      tags:
        - Billing
      description: Returns the email address invoices are sent to and the billing address, when set.
      responses:
        "200":
          description: Billing contact.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlatformBillingContact"
              example:
                invoiceEmail: billing@example.com
                billingAddress:
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        default:
          $ref: "#/components/responses/Error"
  /commerce/billing/payment-method:
    get:
      summary: Check for a saved card
      operationId: platformGetPaymentMethodStatus
      tags:
        - Billing
      description: >-
        Reports whether your organization has a saved card that Ornn can charge after credits run out. It never
        returns card details. Save or replace a card in the console.
      responses:
        "200":
          description: Saved-card status.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlatformPaymentMethodStatus"
              example:
                saved: true
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        default:
          $ref: "#/components/responses/Error"
  /v1/ledger/accounts:
    get:
      summary: List account balances
      operationId: platformListAccountBalances
      tags:
        - Billing
      description: >-
        Returns your organization's account balances in signed integer cents. Your available Ornn credits are the
        negated ORNN_CREDIT balance for a currency, floored at zero. Requires an approved organization.
      responses:
        "200":
          description: Account balances.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlatformAccountBalances"
              example:
                accounts:
                  - account:
                      organization_id: 22222222-2222-4222-8222-222222222222
                      account_role: ORNN_CREDIT
                      currency: usd
                    balance_cents: "-25000"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        default:
          $ref: "#/components/responses/Error"
  /v1/ledger/transactions:
    get:
      summary: List account transactions
      operationId: platformListAccountTransactions
      tags:
        - Billing
      description: >-
        Returns a page of your organization's account transactions, newest recorded first, with only your
        own postings. Events recorded for another party show only their type, time, and reservation.
        Pass next_cursor as cursor with the same filters to read older entries. An empty next_cursor
        marks the end. Omit cursor to restart from the newest entries. Pages are not a snapshot across
        requests. Requires an approved organization.
      parameters:
        - name: limit
          in: query
          required: false
          description: Maximum transactions per page.
          schema:
            type: integer
            minimum: 1
            maximum: 500
            default: 500
        - name: cursor
          in: query
          required: false
          description: Opaque next_cursor from the previous page. Keep the same reservation filter.
          schema:
            type: string
            minLength: 1
            maxLength: 128
        - name: reservation_id
          in: query
          required: false
          description: Only transactions for this reservation.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Transactions.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlatformAccountTransactions"
              example:
                next_cursor: ""
                transactions:
                  - ledger_transaction_id: eeeeeeee-eeee-4eee-8eee-eeeeeeeeeeee
                    event:
                      event_type: credit_applied
                      source_id: example-source
                      organization_id: 22222222-2222-4222-8222-222222222222
                      invoice_id:
                      payment_intent_id: ""
                      reservation_id: 11111111-1111-4111-8111-111111111111
                      occurred_at: "2026-10-14T12:00:00Z"
                      reverses_transaction_id:
                    recorded_at: "2026-10-14T12:00:01Z"
                    postings:
                      - account:
                          organization_id: 22222222-2222-4222-8222-222222222222
                          account_role: ORNN_CREDIT
                          currency: usd
                        amount_cents: 2500
        "400":
          description: An unexpected or malformed query parameter. Invalid limits and cursors include detail.code and detail.message.
          content:
            application/json:
              schema:
                anyOf:
                  - $ref: "#/components/schemas/PlatformPlainError"
                  - $ref: "#/components/schemas/Error"
              example:
                detail:
                  code: invalid_cursor
                  message: The cursor is invalid. Start again without a cursor.
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        default:
          $ref: "#/components/responses/Error"
components:
  securitySchemes:
    OrnnApiKey:
      type: http
      scheme: bearer
      bearerFormat: ornn_sk_...
      description: Create an organization API key in Account Settings > Developer. Do not send this key to the Kubernetes API server.
  responses:
    Error:
      description: "Unsuccessful request. Check HTTP status first. Errors may have detail.code/detail.message, a string detail and optional top-level code, or an empty/non-JSON body. 401: invalid credential; 403: access or approval required; 404: not visible or credentials not ready; 409: lifecycle conflict; 429: back off; 5xx: service failure. Inspect current state before retrying mutations."
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
    Unauthorized:
      description: The credential is missing, invalid, revoked, or no longer belongs to a current organization member. Check the HTTP status before parsing the body.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
    Forbidden:
      description: The credential lacks access to this operation, or the organization requires approval. An API key does not bypass these checks.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
    AccountRateLimited:
      description: Too many requests. Wait for the number of seconds in Retry-After, or at least 60 seconds when the header is absent, then retry with jitter.
      headers:
        Retry-After:
          description: Seconds to wait before retrying.
          schema:
            type: integer
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/AccountRateLimitError"
          example:
            error: rate_limited
            retry_after: 42
            detail: Too many requests. Try again in 42 seconds.
    AccountReservationNotFound:
      description: The reservation does not exist or is not held by your organization.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
    AccountReservationNotLaunchable:
      description: The reservation has ended, was cancelled, or has a pending change, so its keys cannot change.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
          example:
            detail:
              code: reservation_not_launchable
              message: This reservation cannot launch in its current state.
    PlatformVolumeNotFound:
      description: The volume does not exist or is not one of your volumes.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
          example:
            detail:
              code: storage_drive_not_found
              message: Ornn could not find that volume.
    PlatformVolumeMounted:
      description: The volume is mounted on a machine. Detach it first.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
          example:
            detail:
              code: storage_drive_mounted
              message: This volume is in use on a machine. Unmount it first.
    PlatformGithubConnectionNotFound:
      description: The GitHub connection is not one of yours.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
          example:
            detail:
              code: storage_github_connection_not_found
              message: Ornn has no GitHub account linked for this. Connect GitHub in Account settings, then try again.
    PlatformNetworkNotFound:
      description: The network does not exist or belongs to another organization.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
          example:
            detail:
              code: network_not_found
              message: Ornn could not find that network. It may have been deleted. Refresh and choose another.
    PlatformPeerNotFound:
      description: The WireGuard config does not exist or was already removed.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
          example:
            detail:
              code: vpn_peer_not_found
              message: Ornn could not find that WireGuard config. It may already be removed.
    PlatformMachineNotVisible:
      description: >-
        The machine is not in a reservation your organization holds, or the reservation was cancelled.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/PlatformPlainError"
          example:
            error: not_found
    PlatformTelemetryBadRequest:
      description: Invalid start, end, or max_points.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/PlatformPlainError"
          example:
            error: max_points_out_of_range
    PlatformPageBadRequest:
      description: Invalid limit or cursor.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/PlatformPlainError"
          example:
            error: limit must be a positive integer
  schemas:
    Session:
      type: object
      required:
        - user_id
        - email
        - role
        - organizations
        - impersonating
      properties:
        user_id:
          type: string
          format: uuid
        email:
          type: string
        name:
          type:
            - string
            - "null"
        role:
          type: string
          description: API keys always resolve as user.
        impersonating:
          type: boolean
        organizations:
          type: array
          items:
            type: object
            required:
              - id
            properties:
              id:
                type: string
                format: uuid
              name:
                type: string
              approved:
                type: boolean
              role:
                type: string
    Reservation:
      type: object
      required:
        - reservationId
        - status
        - isSpot
        - reservedNodeCount
        - reservedGpuCount
        - startsAt
        - endsAt
      properties:
        reservationId:
          type: string
          format: uuid
        customerId:
          type: string
          format: uuid
        listingId:
          type: string
          format: uuid
        listingTermId:
          type: string
          format: uuid
        status:
          type: string
          enum:
            - confirmed
            - active
            - completed
            - cancelled
        isSpot:
          type: boolean
        provisioningType:
          type: string
          enum:
            - virtual-machine
            - bare-metal
            - kubernetes
            - slurm
        facilityId:
          type: string
          format: uuid
        gpuSlug:
          type: string
        gpusPerNode:
          type: integer
        reservedNodeCount:
          type: integer
        reservedGpuCount:
          type: integer
        startsAt:
          type: string
          format: date-time
        endsAt:
          type:
            - string
            - "null"
          format: date-time
        pricePerGpuHourMicroUsd:
          type: integer
          format: int64
          description: Your agreed reservation rate in integer micro-USD per GPU-hour, including any fee you pay. Historical reservations without a saved rate show the linked listing term price.
        durationHours:
          type:
            - number
            - "null"
          description: Original agreed duration in hours, separate from operational start and end dates. Renewable reservations retain this duration with a null end date. Null when no duration was agreed.
        renewable:
          type:
            - boolean
            - "null"
          description: Whether the reservation contract permits renewal.
        downPaymentCents:
          type:
            - integer
            - "null"
          format: int64
        pendingOperation:
          type:
            - string
            - "null"
        pendingOperationId:
          type: string
          format: uuid
        vmDiskId:
          type:
            - string
            - "null"
          format: uuid
        deployedAt:
          type:
            - string
            - "null"
          format: date-time
        endReason:
          type:
            - string
            - "null"
        transactionId:
          type: string
          format: uuid
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
        relistableNodeCount:
          type: integer
        relistUnavailableReason:
          type:
            - string
            - "null"
        relistableNodeIds:
          type: array
          items:
            type: string
            format: uuid
    ReservationPage:
      type: object
      required:
        - reservations
      properties:
        reservations:
          type: array
          items:
            $ref: "#/components/schemas/Reservation"
        nextCursor:
          type:
            - string
            - "null"
          description: Pass unchanged as cursor on the next request. Absent, null, or empty means the last page.
    Node:
      type: object
      required:
        - node_id
        - reservation_id
        - controller_id
        - status
      properties:
        node_id:
          type: string
          format: uuid
        reservation_id:
          type:
            - string
            - "null"
          format: uuid
        controller_id:
          type:
            - string
            - "null"
          format: uuid
          description: The cluster currently holding this node, or null.
        status:
          type: string
          description: Node lifecycle or access mode, such as bare-metal; not a liveness signal.
        health_readiness:
          $ref: "#/components/schemas/NodeHealthReadiness"
        gpu_type:
          type:
            - string
            - "null"
        gpu_count:
          type:
            - integer
            - "null"
        hostname:
          type:
            - string
            - "null"
        facility_id:
          type:
            - string
            - "null"
          format: uuid
        cpu_type:
          type:
            - string
            - "null"
        memory_bytes:
          type:
            - integer
            - "null"
          format: int64
        storage_bytes:
          type:
            - integer
            - "null"
          format: int64
        ip:
          type: string
        port:
          type:
            - integer
            - "null"
        username:
          type: string
        ssh_endpoint:
          type: string
        enrolled_at:
          type: string
          format: date-time
        cleanup_state:
          type: string
    NodeList:
      type: object
      required:
        - nodes
      properties:
        nodes:
          type: array
          items:
            $ref: "#/components/schemas/Node"
    NodeHealthReadiness:
      type: object
      description: Heartbeat-based launch preflight returned on reservation nodes. A stale heartbeat blocks launch. Missing heartbeat evidence returns ready true with heartbeat:unknown in advisory_reasons; this is not proof of host reachability, GPU health, or workload readiness. Treat an absent readiness object as unknown, not as ready. The reservation-node serializer returns an object, never null.
      required:
        - ready
        - status
        - reasons
        - advisory_reasons
      properties:
        ready:
          type: boolean
          description: Whether the heartbeat preflight reports no blocking reason.
        status:
          type: string
          enum:
            - ready
            - blocked
        reasons:
          type: array
          items:
            type: string
          description: Blocking reasons, such as heartbeat:stale.
        advisory_reasons:
          type: array
          items:
            type: string
          description: Nonblocking advisories, such as heartbeat:unknown.
      example:
        ready: false
        status: blocked
        reasons:
          - heartbeat:stale
        advisory_reasons: []
    CreateCluster:
      type: object
      required:
        - kind
        - reservation_id
      properties:
        kind:
          type: string
          enum:
            - kubernetes
            - slurm
          description: The cluster type to launch.
        reservation_id:
          type: string
          format: uuid
          description: An active reservation your organization holds. A repeat request for the same reservation returns the same cluster. Use reservationId from the reservation list, not a listing ID.
        network_mode:
          type: string
          enum:
            - public
            - private
          default: public
          description: public exposes the Kubernetes API or Slurm SSH login on the internet, with authentication. private requires a WireGuard connection to your organization's private network.
        network_id:
          type:
            - string
            - "null"
          format: uuid
          description: An existing organization network for private access. Used only when network_mode is private. Omit it to use the shared default network; this does not create a network.
        node_ids:
          type: array
          items:
            type: string
            format: uuid
          description: Host IDs (node_id from the reservation node list), not GPU IDs. Each host must be in bare-metal status, not in another cluster, and on an active reservation your organization owns. Ornn prepares each host before it joins, which stops its processes, wipes the tenant home directory, and removes any VM on it. If this and virtual_machine_ids are both omitted or empty, every bare-metal host on reservation_id is used.
        virtual_machine_ids:
          type: array
          items:
            type: string
            format: uuid
          description: Existing VMs on reservations your organization owns to use as workers. A VM must not be deleted, destroyed, or offline. Do not list a VM and its host in node_ids in the same request.
        node_count:
          type:
            - integer
            - "null"
          description: Optional expected host count; it never selects or limits hosts. Kubernetes ignores it. Slurm rejects the launch with 409 cluster_nodes_requested_unavailable when it differs from the number of hosts that resolved. Omit it, or send exactly the number of hosts you list.
        ssh_key_ids:
          type: array
          items:
            type: string
            format: uuid
          description: Slurm only. Organization SSH key IDs (ssh_keys[].id from GET /v1/organizations/{organization_id}/ssh-keys) to authorize on the login node. Omit to authorize every active key in your organization; with no active key, Slurm launch returns 409 slurm_ssh_key_required. Kubernetes ignores this field.
        cluster_name:
          type:
            - string
            - "null"
          deprecated: true
          description: Accepted for compatibility and ignored.
      additionalProperties: false
    Progress:
      type: object
      description: Stages of the latest launch or teardown. Launch stages are create_plane, prepare_hosts, join_workers, apply_plane, and issue_access. Teardown stages include workers and the control-plane stage (namespace for Kubernetes, stack for Slurm), plus nodes or storage when those steps fail.
      properties:
        stages:
          type: array
          items:
            type: object
            properties:
              key:
                type: string
                description: Stable stage identifier.
              label:
                type: string
                description: Display label for the stage.
              status:
                type: string
                enum:
                  - pending
                  - running
                  - completed
                  - failed
              detail:
                type:
                  - string
                  - "null"
                description: Failure reason or worker confirmation count, when available.
    Cluster:
      type: object
      required:
        - id
        - kind
        - state
        - network_mode
        - credential_state
      properties:
        id:
          type: string
          format: uuid
        kind:
          type: string
          enum:
            - kubernetes
            - slurm
        state:
          type: string
          description: pending or provisioning while launching; active once every launch worker has joined and access has been issued; failed when a launch or teardown did not finish; deleting during teardown. A cluster whose teardown finished is no longer listed. Treat unrecognized values as not ready.
        network_mode:
          type: string
          enum:
            - public
            - private
        network_id:
          type:
            - string
            - "null"
          format: uuid
          description: The dedicated organization network for a private cluster, or null for the shared default.
        endpoint:
          type:
            - string
            - "null"
          description: Kubernetes API server URL, or the Slurm login host, while state is active; otherwise null.
        credential_state:
          type: string
          description: pending until access is issued, then active. Issued access does not prove Kubernetes worker or GPU readiness.
        metadata:
          type:
            - object
            - "null"
          properties:
            namespace:
              type: string
              description: The namespace Ornn configured for your workloads.
        backend_metadata:
          type:
            - object
            - "null"
          description: The reservation the cluster launched on, under kubernetes_launch or slurm_launch.
          properties:
            kubernetes_launch:
              type: object
              properties:
                reservation_id:
                  type: string
                  format: uuid
            slurm_launch:
              type: object
              properties:
                reservation_id:
                  type: string
                  format: uuid
        progress:
          anyOf:
            - $ref: "#/components/schemas/Progress"
            - type: "null"
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    Worker:
      type: object
      required:
        - node_id
        - role
        - gpu_count
      properties:
        node_id:
          type: string
          format: uuid
        controller_id:
          type:
            - string
            - "null"
          format: uuid
        reservation_id:
          type:
            - string
            - "null"
          format: uuid
        role:
          type: string
          description: Always worker. Ornn runs the control plane separately.
        state:
          type:
            - string
            - "null"
          description: joining while the host joins; ready once it has joined; join_failed if it could not join; draining after a removal request; cleanup_failed if removal cleanup did not finish. Can be null. A host being prepared after an add request is not listed until preparation finishes. For Kubernetes, verify Node readiness and allocatable GPUs through the cluster API.
        gpu_count:
          type: integer
          description: GPUs on this host.
        linux_username:
          type:
            - string
            - "null"
        ssh_port:
          type:
            - integer
            - "null"
    ClusterList:
      type: object
      required:
        - controllers
      properties:
        controllers:
          type: array
          items:
            type: object
            required:
              - controller
              - nodes
            properties:
              controller:
                $ref: "#/components/schemas/Cluster"
              nodes:
                type: array
                items:
                  $ref: "#/components/schemas/Worker"
    ClusterAccepted:
      type: object
      required:
        - id
        - state
      properties:
        id:
          type: string
          format: uuid
        kind:
          type: string
        organization_id:
          type:
            - string
            - "null"
          format: uuid
        access_mode:
          type: string
        state:
          type: string
        network_mode:
          type: string
        endpoint:
          type:
            - string
            - "null"
        credential_state:
          type: string
        namespace:
          type:
            - string
            - "null"
          description: The namespace Ornn configures for your workloads.
        node_count:
          type: integer
          description: Workers attached when this response was built; usually 0 right after a launch.
        gpu_count:
          type: integer
          description: GPUs on the workers counted in node_count.
        progress:
          anyOf:
            - $ref: "#/components/schemas/Progress"
            - type: "null"
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    KubernetesAccess:
      type: object
      required:
        - kind
        - kubeconfig
        - endpoint
        - namespace
      properties:
        kind:
          type: string
          enum:
            - kubernetes
        kubeconfig:
          type: string
          description: Sensitive kubeconfig YAML whose service-account token has cluster-admin rights and is issued for seven days. Store it with owner-only permissions. Reading this endpoint again returns the same token; it does not renew it.
        endpoint:
          type: string
          description: The cluster's Kubernetes API server URL. Send Kubernetes API calls here, not to api.ornn.com.
        namespace:
          type: string
          description: The namespace Ornn configured for your workloads, also the kubeconfig's default. Pods created here are scheduled onto your reserved workers and count against its GPU ResourceQuota.
    SlurmAccess:
      type: object
      required:
        - kind
        - host
        - port
        - login_host
        - login_port
      properties:
        kind:
          type: string
          enum:
            - slurm
        host:
          type: string
          description: Login host as stored when access was issued. Prefer login_host.
        node_id:
          type:
            - string
            - "null"
          format: uuid
          description: The worker host that serves the login endpoint, when it runs on one.
        port:
          type: integer
          description: Login port as stored when access was issued. Prefer login_port.
        ssh_host_key:
          type:
            - string
            - "null"
          description: The login node's ed25519 public host key. Compare it with the key SSH shows on first connection, or pin it in known_hosts.
        login_user:
          type:
            - string
            - "null"
          description: Linux user for SSH login.
        ssh_command:
          type:
            - string
            - "null"
          description: A ready-made ssh command with the login user, host, and port, when all three are known.
        login_host:
          type: string
          description: Host to SSH to.
        login_port:
          type: integer
          description: Port to SSH to. It is cluster-specific and is usually not 22.
        authorized_key_fingerprints:
          type:
            - array
            - "null"
          items:
            type: string
          description: SHA256 fingerprints of the organization SSH keys authorized on the login node.
    WorkerChange:
      type: object
      required:
        - controller_id
        - state
      properties:
        controller_id:
          type: string
          format: uuid
        node_id:
          type: string
          format: uuid
        virtual_machine_id:
          type: string
          format: uuid
        state:
          type: string
          description: On a new addition, cleaning while Ornn prepares the host or joining when it attaches directly. On a repeated addition, the current membership state, such as joining or ready, or cleaning while preparation is still pending. On removal, draining.
    Error:
      type: object
      properties:
        code:
          type: string
        detail:
          oneOf:
            - type: string
            - type: object
              required:
                - code
                - message
              properties:
                code:
                  type: string
                message:
                  type: string
    AccessReservationDiskRequest:
      type: object
      properties:
        vmDiskId:
          type:
            - string
            - "null"
          format: uuid
          description: An Ornn disk or one of your organization's disks. Null clears the choice.
      additionalProperties: false
    AccessLaunchRequest:
      type: object
      properties:
        access_mode:
          type: string
          enum:
            - virtual-machine
            - bare-metal
          description: Defaults to the reservation's access mode. Use the mode your reservation was booked with.
        disk_id:
          type:
            - string
            - "null"
          format: uuid
          description: VM only. Overrides the reservation's saved disk for VMs this request creates. When omitted, the saved disk is used; a VM launch with no disk available fails with disk_required. Must be omitted for Bare Metal.
        network_mode:
          type: string
          enum:
            - public
            - private
          default: public
          description: public exposes SSH on the host's public address. private connects the machine to your organization's private network.
        network_id:
          type:
            - string
            - "null"
          format: uuid
          description: Private networking only. An existing private network in your organization. Omit it to use the organization's shared network. This does not create a network.
        node_count:
          type:
            - integer
            - "null"
          minimum: 1
          description: How many of the reservation's nodes to provision when launch starts provisioning. Defaults to every reserved node and cannot exceed it. Ignored when the reservation already has machines.
        ssh_key_ids:
          type: array
          uniqueItems: true
          items:
            type: string
            format: uuid
          description: Active SSH keys in your organization to authorize. The keys are also attached to the reservation. Omit to use the reservation's current keys, or every active organization key when none are selected.
        request_id:
          type:
            - string
            - "null"
          description: Optional client token for this request. Reuse it when you retry the same launch so Ornn can recognize repeated commands.
    AccessSshKeyDelivery:
      type: object
      description: Delivery state of one SSH key on a host.
      properties:
        fingerprint:
          type: string
          description: SHA256 fingerprint of the public key.
        label:
          type:
            - string
            - "null"
        status:
          type: string
          description: installed or failed.
        linux_username:
          type: string
        pushed_at:
          type:
            - string
            - "null"
          format: date-time
        failed_at:
          type:
            - string
            - "null"
          format: date-time
        failure_reason:
          type:
            - string
            - "null"
        request_id:
          type:
            - string
            - "null"
    AccessRebootStatus:
      type: object
      description: Progress of a host reboot. Hard resets are not tracked here.
      required:
        - packet_id
        - status
        - started_at
      properties:
        packet_id:
          type: string
          format: uuid
          description: Identifier of the reboot request.
        status:
          type: string
          enum:
            - in_progress
            - succeeded
            - failed
        started_at:
          type: string
          format: date-time
        detail:
          description: Why the reboot failed, when status is failed.
          oneOf:
            - type: "null"
            - type: object
              properties:
                code:
                  type: string
                message:
                  type: string
    AccessNode:
      description: A reserved host node. Fields not listed under Node are SSH key delivery and reboot state.
      allOf:
        - $ref: "#/components/schemas/Node"
        - type: object
          properties:
            authorized_key_fingerprints:
              type: array
              items:
                type: string
              description: Fingerprints of SSH keys installed for the tenant login on this host.
            authorized_key_metadata:
              type: array
              items:
                $ref: "#/components/schemas/AccessSshKeyDelivery"
            keys_pushed_at:
              type:
                - string
                - "null"
              format: date-time
              description: When keys were last installed on this host.
            reboot:
              description: The latest tracked host reboot, or null.
              oneOf:
                - type: "null"
                - $ref: "#/components/schemas/AccessRebootStatus"
    AccessNodeList:
      type: object
      required:
        - nodes
      properties:
        nodes:
          type: array
          items:
            $ref: "#/components/schemas/AccessNode"
    AccessRebootRequest:
      type: object
      properties:
        mode:
          type: string
          enum:
            - reboot
            - hard_reset
          default: reboot
          description: reboot keeps data on the host. hard_reset wipes tenant data on the host before rebooting.
        request_id:
          type:
            - string
            - "null"
          description: Optional client token. A repeated request with the same request_id returns the original request instead of rebooting again.
    AccessRebootResult:
      type: object
      required:
        - id
        - node_id
        - reservation_id
        - packet_id
        - mode
      properties:
        id:
          type: string
          format: uuid
          description: The ID you passed in the path.
        node_id:
          type: string
          format: uuid
          description: The host node being rebooted.
        reservation_id:
          type: string
          format: uuid
        packet_id:
          type: string
          format: uuid
          description: Identifier of the queued request.
        mode:
          type: string
          enum:
            - reboot
            - hard_reset
        reboot:
          description: Reboot progress, or null for a hard reset.
          oneOf:
            - type: "null"
            - $ref: "#/components/schemas/AccessRebootStatus"
    AccessVirtualMachineStage:
      type: object
      required:
        - key
        - label
        - status
      properties:
        key:
          type: string
          description: One of prepare_host, prepare_gpu, prepare_disk, start_vm, verify_readiness, configure_storage, or configure_access. Stages that have not started are omitted.
        label:
          type: string
          description: Display label, such as Starting virtual machine.
        status:
          type: string
          enum:
            - pending
            - running
            - completed
            - failed
        started_at:
          type:
            - string
            - "null"
          format: date-time
        completed_at:
          type:
            - string
            - "null"
          format: date-time
    AccessVirtualMachine:
      type: object
      required:
        - id
        - node_id
        - reservation_id
        - status
        - stages
        - gpu_count
        - created_at
      properties:
        id:
          type: string
          format: uuid
          description: VM id. Reboot a VM and reimage take this ID with its node_id. The host reboot and hard reset route takes a node ID, not this ID.
        node_id:
          type: string
          format: uuid
          description: The host node running the VM.
        reservation_id:
          type: string
          format: uuid
        status:
          type: string
          description: provisioning, running, failed, or deleting. Treat any other value as not ready.
        operation:
          type:
            - string
            - "null"
          description: The latest operation, provision, destroy, or reboot. A VM stays running during a reboot.
        operation_id:
          type:
            - string
            - "null"
          format: uuid
        operation_status:
          type:
            - string
            - "null"
          description: queued, running, succeeded, or failed.
        operation_started_at:
          type:
            - string
            - "null"
          format: date-time
        operation_completed_at:
          type:
            - string
            - "null"
          format: date-time
        stages:
          type: array
          description: Customer launch progress for the latest provision, in order.
          items:
            $ref: "#/components/schemas/AccessVirtualMachineStage"
        failure:
          description: Present when the VM or its latest operation failed. code is virtual_machine_provision_failed, virtual_machine_cleanup_failed, or virtual_machine_reboot_failed.
          oneOf:
            - type: "null"
            - type: object
              required:
                - code
                - message
              properties:
                code:
                  type: string
                message:
                  type: string
        ready_at:
          type:
            - string
            - "null"
          format: date-time
          description: When the VM first became ready. Set only while running.
        hostname:
          type: string
          description: The VM's hostname. Not an SSH address.
        ip:
          type:
            - string
            - "null"
          description: SSH address. Present while provisioning or running.
        tenant_username:
          type:
            - string
            - "null"
          description: Linux login, tenant. Present only while running.
        port:
          type:
            - integer
            - "null"
          description: SSH port. Present while provisioning or running. Do not assume 22.
        gpu_count:
          type: integer
        disk_id:
          type:
            - string
            - "null"
          format: uuid
        created_at:
          type: string
          format: date-time
        destroy_at:
          type:
            - string
            - "null"
          format: date-time
    AccessVirtualMachineList:
      type: object
      required:
        - virtual_machines
      properties:
        virtual_machines:
          type: array
          items:
            $ref: "#/components/schemas/AccessVirtualMachine"
    AccountSshKey:
      type: object
      required:
        - id
        - organization_id
        - public_key
        - fingerprint
        - status
        - created_at
      properties:
        id:
          type: string
          format: uuid
        organization_id:
          type: string
          format: uuid
          description: Your organization.
        tenant_id:
          type: string
          format: uuid
          deprecated: true
          description: Same value as organization_id. Use organization_id.
        public_key:
          type: string
          description: The OpenSSH public key as registered, including any comment.
        fingerprint:
          type: string
          description: SHA-256 fingerprint in OpenSSH format, such as SHA256:...
        label:
          type:
            - string
            - "null"
          description: Your name for the key.
        status:
          type: string
          enum:
            - active
            - revoked
        created_at:
          type: string
          format: date-time
        revoked_at:
          type:
            - string
            - "null"
          format: date-time
    AccountSshKeyRegistration:
      allOf:
        - $ref: "#/components/schemas/AccountSshKey"
        - type: object
          required:
            - outcome
          properties:
            outcome:
              type: string
              enum:
                - created
                - already_registered
                - reactivated
              description: created for a new key; already_registered when an active key with this fingerprint existed and was returned unchanged; reactivated when a revoked key became active again.
    AccountSshKeyPage:
      type: object
      required:
        - ssh_keys
        - has_revoked
      properties:
        ssh_keys:
          type: array
          items:
            $ref: "#/components/schemas/AccountSshKey"
        next_cursor:
          type:
            - string
            - "null"
          description: Pass unchanged as cursor for the next page. Null means the last page.
        has_revoked:
          type: boolean
          description: Whether the organization has any revoked keys.
    AccountCreateSshKey:
      type: object
      required:
        - public_key
      properties:
        public_key:
          type: string
          description: One line containing an OpenSSH public key. Supported types are ssh-ed25519, ecdsa-sha2-nistp256, ecdsa-sha2-nistp384, ecdsa-sha2-nistp521, sk-ssh-ed25519@openssh.com, sk-ecdsa-sha2-nistp256@openssh.com, ssh-rsa, rsa-sha2-256, and rsa-sha2-512.
        label:
          type:
            - string
            - "null"
          description: Optional name for the key. Leading and trailing spaces are removed.
    AccountSshKeyRevocation:
      type: object
      required:
        - ssh_key
        - machines
      properties:
        ssh_key:
          $ref: "#/components/schemas/AccountSshKey"
        machines:
          type: array
          items: {}
          description: Always empty. Check each node to confirm removal.
    AccountReservationSshKeys:
      type: object
      required:
        - ssh_keys
      properties:
        ssh_keys:
          type: array
          items:
            $ref: "#/components/schemas/AccountSshKey"
    AccountAssignReservationSshKey:
      oneOf:
        - type: object
          title: Existing key
          required:
            - ssh_key_id
          properties:
            ssh_key_id:
              type: string
              format: uuid
              description: An active organization SSH key.
          additionalProperties: false
        - type: object
          title: New public key
          required:
            - public_key
          properties:
            public_key:
              type: string
              description: One-line OpenSSH public key to register and assign.
            label:
              type:
                - string
                - "null"
          additionalProperties: false
    AccountAssignedSshKey:
      allOf:
        - $ref: "#/components/schemas/AccountSshKey"
        - type: object
          properties:
            outcome:
              type: string
              enum:
                - created
                - already_registered
                - reactivated
              description: Present when the request sent a new public key.
    AccountPushSshKeys:
      type: object
      required:
        - ssh_key_ids
      properties:
        ssh_key_ids:
          type: array
          minItems: 1
          uniqueItems: true
          items:
            type: string
            format: uuid
          description: Active organization keys to assign to the machine's reservation before delivery.
        request_id:
          type: string
          description: Optional delivery ID for your own correlation. Omit it to let Ornn generate one. If you send it, use a new value for every push; reusing a value for the same machine returns the earlier delivery instead of queuing the current key set.
      additionalProperties: false
    AccountPushSshKeysResult:
      type: object
      required:
        - id
        - node_id
        - reservation_id
        - packet_ids
        - ssh_key_ids
      properties:
        id:
          type: string
          format: uuid
          description: The machine ID from the request path.
        node_id:
          type: string
          format: uuid
          description: The physical node that hosts the machine.
        reservation_id:
          type: string
          format: uuid
        packet_id:
          type:
            - string
            - "null"
          format: uuid
          description: The first queued delivery ID, or null when nothing was queued.
        packet_ids:
          type: array
          items:
            type: string
            format: uuid
          description: One queued delivery ID per updated VM or host.
        ssh_key_ids:
          type: array
          items:
            type: string
            format: uuid
          description: The reservation's full key set that was queued.
    AccountRateLimitError:
      type: object
      properties:
        error:
          type: string
          enum:
            - rate_limited
        retry_after:
          type: integer
          description: Seconds to wait before retrying.
        detail:
          type: string
    CapacityRequestBodyError:
      type: object
      description: Returned for a malformed JSON body or an unknown field.
      properties:
        error:
          type: string
      example:
        error: invalid request body
    CapacitySpotBook:
      type: object
      required:
        - gpuSlug
        - orderBookId
      properties:
        gpuSlug:
          type: string
          description: Catalog GPU slug of the book.
        orderBookId: *id004
        bestAskPricePerGpuHourMicroUsd:
          type:
            - integer
            - "null"
          format: int64
          description: Lowest ask, as a buyer pays it (fees included).
        bestBidPricePerGpuHourMicroUsd:
          type:
            - integer
            - "null"
          format: int64
          description: Highest bid, as a seller receives it (fees removed).
    CapacitySpotBookList:
      type: object
      required:
        - orderBooks
      properties:
        orderBooks:
          type: array
          items:
            $ref: "#/components/schemas/CapacitySpotBook"
    CapacitySpotLevel:
      type: object
      required:
        - pricePerGpuHourMicroUsd
        - gpus
        - orders
      properties:
        pricePerGpuHourMicroUsd:
          type: integer
          format: int64
          description: Asks show what a buyer pays; bids show what a seller receives.
        gpus:
          type: integer
          description: GPUs resting at this price.
        orders:
          type: integer
          description: Orders resting at this price.
    CapacitySpotDepth:
      type: object
      required:
        - gpuSlug
        - orderBookId
        - asks
        - bids
      properties:
        gpuSlug:
          type: string
        orderBookId: *id004
        asks:
          type: array
          description: Up to 50 ask levels, lowest price first.
          items:
            $ref: "#/components/schemas/CapacitySpotLevel"
        bids:
          type: array
          description: Up to 50 bid levels, highest price first.
          items:
            $ref: "#/components/schemas/CapacitySpotLevel"
    CapacitySpotTrade:
      type: object
      required:
        - pricePerGpuHourMicroUsd
        - gpus
        - aggressorSide
        - filledAt
      properties:
        pricePerGpuHourMicroUsd:
          type: integer
          format: int64
          description: The price of the level the trade took.
        gpus:
          type: integer
        aggressorSide:
          type: string
          enum:
            - bid
            - ask
          description: bid when an incoming bid took an ask; ask when new capacity filled a resting bid.
        filledAt: &id013
          type: string
          format: date-time
    CapacitySpotTradeList:
      type: object
      required:
        - gpuSlug
        - trades
      properties:
        gpuSlug:
          type: string
        trades:
          type: array
          description: Up to 50 recent fills, newest first. Trades never identify either party.
          items:
            $ref: "#/components/schemas/CapacitySpotTrade"
    CapacitySpotQuoteFill:
      type: object
      required:
        - pricePerGpuHourMicroUsd
        - interruptsAt
        - interruptReason
      properties:
        pricePerGpuHourMicroUsd:
          type: integer
          format: int64
          description: What you would pay per GPU-hour, fees included.
        interruptsAt:
          type:
            - string
            - "null"
          format: date-time
          description: When spot on the matched node is next scheduled to end. Counts only reservations already sold and the end of the relisted capacity.
        interruptReason:
          type: string
          enum:
            - reservation
            - listing_end
          description: "reservation: a sold window starts on the node. listing_end: the relisted capacity ends."
    CapacitySpotQuote:
      type: object
      required:
        - gpuSlug
        - fills
      properties:
        gpuSlug:
          type: string
        fills:
          type: array
          description: One entry per bid that would fill now, in order. Fewer entries than requested bids means the rest would rest (limit) or be cancelled (market).
          items:
            $ref: "#/components/schemas/CapacitySpotQuoteFill"
    CapacitySpotBidRequest:
      type: object
      required:
        - gpuSlug
        - gpus
        - orderType
      additionalProperties: false
      properties:
        gpuSlug:
          type: string
          description: Catalog GPU slug of the order book.
        gpus:
          type: integer
          enum:
            - 1
            - 2
            - 4
            - 8
          description: "GPUs for one VM on one node. To buy N VMs, place N bids with N keys."
        orderType:
          type: string
          enum:
            - limit
            - market
          description: limit fills at or below your price or rests on the book until it fills or you cancel. market fills now at the best available ask or is cancelled.
        pricePerGpuHourMicroUsd:
          type: integer
          format: int64
          minimum: 1
          description: Required for limit bids; omit for market bids. The most you pay per GPU-hour, fees included, in integer micro-USD (1000000 is $1.00).
    CapacitySpotOrder:
      type: object
      required:
        - orderId
        - side
        - status
        - isMine
        - isSeller
      properties:
        orderId:
          type: string
          format: uuid
          description: Order ID. A filled bid's reservation has this same ID.
        orderBookId: *id004
        gpuSlug:
          type:
            - string
            - "null"
        side:
          type: string
          enum:
            - bid
            - ask
          description: bid for your bids; ask for capacity your listings offer on the book.
        orderType:
          type: string
          enum:
            - limit
            - market
        pricePerGpuHourMicroUsd:
          type:
            - integer
            - "null"
          format: int64
          description: "The order's own price: a bid's limit, or null for a market bid. A fill's price is on the reservation."
        requestedGpus:
          type:
            - integer
            - "null"
        remainingGpus:
          type:
            - integer
            - "null"
        status:
          type: string
          description: "Bids: resting (waiting on the book), running (filled; the spot reservation is live), cancelled (never filled), or ended (a running reservation stopped). Asks: resting, exhausted, or cancelled. Handle unrecognized values."
        cancelReason:
          type:
            - string
            - "null"
          description: For example unfilled_market_order, cancelled_by_owner, or cancelled_by_admin.
        aggressorSide:
          type:
            - string
            - "null"
          enum:
            - bid
            - ask
            -
        listingId:
          type:
            - string
            - "null"
          format: uuid
          description: The listing that filled the bid.
        nodeId:
          type:
            - string
            - "null"
          format: uuid
          description: The node the filled bid runs on.
        createdAt:
          type:
            - string
            - "null"
          format: date-time
        filledAt:
          type:
            - string
            - "null"
          format: date-time
        endedAt:
          type:
            - string
            - "null"
          format: date-time
        endReason:
          type:
            - string
            - "null"
          description: "Why a running bid ended: stopped_by_buyer, stopped_by_seller, stopped_by_admin, preempted_by_reservation, listing_ended, payment_failed, or provisioning_failed. Handle unrecognized values."
        failure:
          type:
            - object
            - "null"
          description: Customer-facing failure details for an ended order with a payment or provisioning failure. Visible only to the buyer and staff; derived from endReason.
          required:
            - code
            - message
          properties:
            code:
              type: string
            message:
              type: string
        isMine:
          type: boolean
          description: True when your organization placed this order.
        isSeller:
          type: boolean
          description: True when the order filled against, or is, capacity your organization listed.
    CapacitySpotOrderEnvelope:
      type: object
      required:
        - order
      properties:
        order:
          $ref: "#/components/schemas/CapacitySpotOrder"
    CapacitySpotOrderList:
      type: object
      required:
        - orders
      properties:
        orders:
          type: array
          description: Your organization's 200 most recent bids, then the asks your listings have on the books. Recently finished orders are retained for a limited time.
          items:
            $ref: "#/components/schemas/CapacitySpotOrder"
    CapacityScheduleTerm:
      type: object
      required:
        - listingTermId
        - pricePerGpuHourMicroUsd
      properties:
        listingTermId: *id004
        type:
          type:
            - string
            - "null"
          enum:
            - reserved
            - spot
            - on-demand
            -
          description: Reserve offers and Market purchases use reserved terms. spot terms are not sold as windows.
        minDurationSeconds:
          type:
            - integer
            - "null"
        maxDurationSeconds:
          type:
            - integer
            - "null"
        pricePerGpuHourMicroUsd:
          type: integer
          format: int64
          description: On a Market listing, the price you pay with fees included. Send this value as askPricePerGpuHourMicroUsd when you buy.
        downPaymentCents:
          type:
            - integer
            - "null"
          format: int64
        renewable:
          type: boolean
    CapacityAvailabilityWindow:
      type: object
      required:
        - startsAt
        - endsAt
        - nodeCount
      properties:
        startsAt: *id013
        endsAt: *id013
        nodeCount:
          type: integer
          description: Nodes still free for this whole interval.
    CapacitySoldWindow:
      type: object
      required:
        - startsAt
        - endsAt
        - isMine
      properties:
        startsAt:
          type:
            - string
            - "null"
          format: date-time
        endsAt:
          type:
            - string
            - "null"
          format: date-time
        isMine:
          type: boolean
          description: True when your organization bought this window.
    CapacityOffer:
      type: object
      required:
        - offerId
        - listingId
        - status
        - isMine
        - onMyListing
      properties:
        offerId:
          type: string
          format: uuid
          description: Offer ID. An accepted offer's reservation has this same ID.
        listingId: *id004
        listingTermId:
          type:
            - string
            - "null"
          format: uuid
        startsAt:
          type:
            - string
            - "null"
          format: date-time
          description: Window start. Acceptance moves it to the acceptance time if that is later.
        endsAt:
          type:
            - string
            - "null"
          format: date-time
        pricePerGpuHourMicroUsd:
          type:
            - integer
            - "null"
          format: int64
        downPaymentCents:
          type:
            - integer
            - "null"
          format: int64
        status:
          type: string
          description: open, accepted, or cancelled. Handle unrecognized values.
        cancelReason:
          type:
            - string
            - "null"
          description: For example cancelled_by_author, cancelled_by_seller, conflict_with_trade (another offer or purchase took the window), or listing_cancelled.
        createdAt:
          type:
            - string
            - "null"
          format: date-time
        acceptedAt:
          type:
            - string
            - "null"
          format: date-time
        isMine:
          type: boolean
          description: True when your organization posted the offer.
        onMyListing:
          type: boolean
          description: True when the offer is on a listing your organization owns.
    CapacitySchedule:
      type: object
      description: A listing as it trades. Prices are what you pay or, for a listing you own, what you receive. Never identifies who supplies the capacity.
      required:
        - listingId
        - listingKind
        - gpuSlug
        - status
        - isMine
      properties:
        listingId: *id004
        listingKind:
          type: string
          description: primary for Reserve listings; secondary for relisted (Market) capacity.
        gpuSlug:
          type: string
        gpusPerNode:
          type:
            - integer
            - "null"
        nodeCount:
          type: integer
        nodeIds:
          type: array
          items:
            type: string
        startsAt:
          type:
            - string
            - "null"
          format: date-time
          description: Start of the listing's availability.
        endsAt:
          type:
            - string
            - "null"
          format: date-time
          description: End of the listing's availability.
        status:
          type: string
          description: active while the listing can trade.
        cancelReason:
          type:
            - string
            - "null"
        acceptedWindows:
          type: array
          description: Windows already sold on this listing.
          items:
            $ref: "#/components/schemas/CapacitySoldWindow"
        capacityWindows:
          type: array
          description: Free node count over time. Use it to choose a window before buying.
          items:
            $ref: "#/components/schemas/CapacityAvailabilityWindow"
        typedTerms:
          type: boolean
          description: True when the listing sells through terms.
        terms:
          type: array
          description: Financial terms. When present, send one listingTermId with an offer or purchase.
          items:
            $ref: "#/components/schemas/CapacityScheduleTerm"
        pricePerGpuHourMicroUsd:
          type:
            - integer
            - "null"
          format: int64
          description: "Reserve listings: the listing price."
        downPaymentCents:
          type:
            - integer
            - "null"
          format: int64
          description: "Reserve listings: the down payment."
        askPricePerGpuHourMicroUsd:
          type:
            - integer
            - "null"
          format: int64
          description: "Market listings: the asking price you pay, fees included."
        spotPricePerGpuHourMicroUsd:
          type:
            - integer
            - "null"
          format: int64
          deprecated: true
          description: Older name for askPricePerGpuHourMicroUsd.
        spotGpusInUse:
          type:
            - integer
            - "null"
          description: GPUs on this listing running Spot VMs now.
        spotGpusOffered:
          type:
            - integer
            - "null"
          description: GPUs on this listing resting on the Spot book.
        isMine:
          type: boolean
          description: True when your organization owns the listing.
        offers:
          type: array
          description: "Single-listing reads only: offers posted on this listing."
          items:
            $ref: "#/components/schemas/CapacityOffer"
    CapacityScheduleList:
      type: object
      required:
        - schedules
      properties:
        schedules:
          type: array
          items:
            $ref: "#/components/schemas/CapacitySchedule"
    CapacityScheduleEnvelope:
      type: object
      required:
        - schedule
      properties:
        schedule:
          $ref: "#/components/schemas/CapacitySchedule"
    CapacityPurchaseLeg:
      type: object
      required:
        - listingId
        - startsAt
        - endsAt
        - askPricePerGpuHourMicroUsd
      additionalProperties: false
      properties:
        listingId:
          type: string
          format: uuid
          description: A Market (secondary) listing.
        listingTermId:
          type: string
          format: uuid
          description: "Required when the listing has terms: a reserved term whose duration bounds allow this window."
        nodeCount:
          type: integer
          minimum: 1
          description: Nodes to buy. Omitted buys every node on the listing.
        startsAt:
          type: string
          format: date-time
          description: Whole-second RFC 3339 start inside the listing, now or later.
        endsAt:
          type: string
          format: date-time
          description: Whole-second RFC 3339 end inside the listing, at least one hour after the start.
        askPricePerGpuHourMicroUsd:
          type: integer
          format: int64
          description: "The price you were shown: the selected term's pricePerGpuHourMicroUsd from the schedule, or askPricePerGpuHourMicroUsd when the listing has no terms. A changed price refuses the purchase with ask_changed."
    CapacityPurchaseRequest:
      type: object
      required:
        - legs
      additionalProperties: false
      properties:
        legs:
          type: array
          minItems: 1
          maxItems: 50
          description: Windows to buy together. All legs succeed or none do; legs may not overlap.
          items:
            $ref: "#/components/schemas/CapacityPurchaseLeg"
    CapacityPurchase:
      type: object
      required:
        - reservationId
        - listingId
        - startsAt
        - endsAt
      properties:
        reservationId:
          type: string
          format: uuid
          description: The reservation this leg creates.
        listingId: *id004
        nodeCount:
          type:
            - integer
            - "null"
        startsAt:
          type:
            - string
            - "null"
          format: date-time
        endsAt:
          type:
            - string
            - "null"
          format: date-time
        askPricePerGpuHourMicroUsd:
          type:
            - integer
            - "null"
          format: int64
          description: What you pay per GPU-hour, fees included.
    CapacityPurchaseList:
      type: object
      required:
        - purchases
      properties:
        purchases:
          type: array
          items:
            $ref: "#/components/schemas/CapacityPurchase"
    CapacityOfferRequest:
      type: object
      required:
        - pricePerGpuHourMicroUsd
      additionalProperties: false
      properties:
        listingTermId:
          type: string
          format: uuid
          description: Required when the listing has terms. Choose a reserved term.
        durationSeconds:
          type: integer
          minimum: 1
          description: Whole seconds within the term's minDurationSeconds and maxDurationSeconds. Required unless the term's minimum equals its maximum.
        pricePerGpuHourMicroUsd:
          type: integer
          format: int64
          minimum: 1
          description: Your offer per GPU-hour in integer micro-USD. It can be below or above the term price.
        downPaymentCents:
          type: integer
          format: int64
          minimum: 0
          description: The down payment you offer, in whole cents. Omitted means 0. The console sends the selected term's downPaymentCents.
    CapacityOfferEnvelope:
      type: object
      required:
        - offer
      properties:
        offer:
          $ref: "#/components/schemas/CapacityOffer"
    CapacityOfferList:
      type: object
      required:
        - offers
      properties:
        offers:
          type: array
          items:
            $ref: "#/components/schemas/CapacityOffer"
    CapacityOfferAccepted:
      type: object
      required:
        - offer
        - reservationId
      properties:
        offer:
          $ref: "#/components/schemas/CapacityOffer"
        reservationId:
          type: string
          format: uuid
          description: The buyer's reservation; equal to the offer ID.
    CapacityListing:
      type: object
      description: A capacity listing's hardware, size, and availability. Read prices from its terms.
      required:
        - capacityListingId
        - kind
        - state
        - gpuSlug
        - gpusPerNode
        - capacityNodeCount
      properties:
        capacityListingId: *id004
        listingNumber:
          type: integer
          description: A short reference number. Use capacityListingId in API paths.
        parentReservationId:
          type:
            - string
            - "null"
          format: uuid
          description: "Set on relisted capacity: the reservation it was relisted from."
        kind:
          type: string
          enum:
            - primary
            - secondary
            - on_demand
          description: primary for Reserve listings; secondary for relisted capacity.
        state:
          type: string
          description: draft, available, held, sold, or delisted. Handle unrecognized values.
        gpuSlug:
          type: string
        gpusPerNode:
          type: integer
        capacityNodeCount:
          type: integer
        facilityId: *id004
        provisioningType:
          type: string
          enum:
            - bare-metal
            - virtual-machine
        availableAt:
          type:
            - string
            - "null"
          format: date-time
        availableUntil:
          type:
            - string
            - "null"
          format: date-time
        daysToRfs:
          type:
            - integer
            - "null"
          description: Readiness lead time in days when the listing has no fixed dates.
        cpu:
          type:
            - string
            - "null"
        ram:
          type:
            - string
            - "null"
        storage:
          type:
            - string
            - "null"
        fabricType:
          type:
            - string
            - "null"
          description: GPU interconnect.
        internet:
          type:
            - string
            - "null"
          description: Internet bandwidth.
        createdAt: *id013
        updatedAt: *id013
    CapacityListingPage:
      type: object
      required:
        - listings
      properties:
        listings:
          type: array
          items:
            $ref: "#/components/schemas/CapacityListing"
        nextCursor:
          type: string
          description: Pass unchanged as cursor for the next page. Absent on the last page.
    CapacityListingTerm:
      type: object
      required:
        - listingTermsId
        - capacityListingId
        - pricePerGpuHourMicroUsd
        - renewable
      properties:
        listingTermsId:
          type: string
          format: uuid
          description: Send as listingTermId in offers and purchases.
        capacityListingId: *id004
        type:
          type:
            - string
            - "null"
          enum:
            - reserved
            - spot
            - on-demand
            -
        renewable:
          type: boolean
        minDurationHours:
          type:
            - number
            - "null"
        maxDurationHours:
          type:
            - number
            - "null"
        downPaymentCents:
          type:
            - integer
            - "null"
          format: int64
        pricePerGpuHourMicroUsd:
          type: integer
          format: int64
          description: The listing's price. On relisted capacity this is the seller's asking price before fees; buyers pay the schedule price instead.
        createdAt: *id013
    CapacityListingTermList:
      type: object
      required:
        - terms
      properties:
        terms:
          type: array
          items:
            $ref: "#/components/schemas/CapacityListingTerm"
    CapacityTermInput:
      type: object
      required:
        - minDurationHours
        - maxDurationHours
        - pricePerGpuHourMicroUsd
        - renewable
      additionalProperties: false
      properties:
        type:
          type: string
          enum:
            - reserved
            - spot
            - on-demand
          description: "reserved (the default) sells fixed windows in the Market. spot offers the nodes' idle time on the Spot order book and requires VM capacity. spot and on-demand are hourly: set both durations to 1 and renewable to true."
        minDurationHours:
          type: number
          exclusiveMinimum: 0
          description: Shortest window a buyer can take, in hours equal to whole seconds.
        maxDurationHours:
          type: number
          exclusiveMinimum: 0
          description: Longest window, in hours equal to whole seconds. Must fit inside the listing window.
        pricePerGpuHourMicroUsd:
          type: integer
          format: int64
          minimum: 1
          description: "Your asking price: what you receive per GPU-hour, in integer micro-USD."
        downPaymentCents:
          type:
            - integer
            - "null"
          format: int64
          minimum: 0
        renewable:
          type: boolean
    CapacityRelistRequest:
      type: object
      required:
        - startsAt
        - nodeCount
        - terms
      additionalProperties: false
      properties:
        startsAt:
          type: string
          format: date-time
          description: Whole-second start, no earlier than the reservation's start.
        endsAt:
          type: [string, "null"]
          format: date-time
          description: Whole-second end after startsAt, no later than the source reservation's committed expiry. Null or omission means no scheduled end and is allowed only when the source has no committed expiry.
        nodeCount:
          type: integer
          minimum: 1
          description: Whole nodes to relist, up to the reservation's relistableNodeCount.
        nodeIds:
          type: array
          items: *id004
          description: Optional. Exactly nodeCount distinct IDs from the reservation's relistableNodeIds. Omit nodeIds to let Ornn choose free assigned nodes.
        terms:
          type: array
          minItems: 1
          items:
            $ref: "#/components/schemas/CapacityTermInput"
    CapacityRelistCreated:
      type: object
      required:
        - listing
        - terms
      properties:
        listing:
          $ref: "#/components/schemas/CapacityListing"
        terms:
          type: array
          items:
            $ref: "#/components/schemas/CapacityListingTerm"
    CapacityListingUpdate:
      type: object
      required:
        - state
      properties:
        state:
          type: string
          enum:
            - draft
            - available
            - delisted
          description: draft takes the listing off sale without delisting it; available puts it back on sale; delisted withdraws it, like the delist operation.
    CapacityListingNodes:
      type: object
      required:
        - nodeIds
      properties:
        nodeIds:
          type: array
          items: *id004
    PlatformPlainError:
      type: object
      description: Error body with a machine-readable string. Check the HTTP status first.
      required:
        - error
      properties:
        error:
          type: string
    PlatformVolumeMount:
      type: object
      description: The volume's mount on one VM or Bare Metal node.
      properties:
        id:
          type: string
          format: uuid
        drive_id:
          type: string
          format: uuid
        reservation_id:
          type:
            - string
            - "null"
          format: uuid
        node_id:
          type:
            - string
            - "null"
          format: uuid
          description: The Bare Metal node or the host delivering commands to the VM.
        virtual_machine_id:
          type:
            - string
            - "null"
          format: uuid
          description: The mounted VM. Null for a Bare Metal mount.
        mount_path:
          type: string
        access:
          type: string
          description: read-write or read-only.
        state:
          type: string
          description: >-
            reserved, provisioning, mounted, release_pending, failed, or stale. mounted means the volume is mounted
            on this VM or Bare Metal node. Handle unrecognized values without assuming readiness.
        error:
          type:
            - string
            - "null"
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    PlatformVolumeGithubRepository:
      type: object
      description: A repository pulled into a folder of the volume.
      properties:
        folder:
          type: string
        full_name:
          type: string
        branch:
          type: string
        connection_id:
          type: string
          format: uuid
        sync:
          type: object
          description: The latest pull or sync of this folder.
          properties:
            state:
              type: string
              description: queued, running, ready, or failed.
            step:
              type: string
              description: While running, checking, downloading, writing, or promoting.
            requested_at:
              type: string
              format: date-time
            commit_sha:
              type: string
            error:
              type:
                - string
                - "null"
              description: A customer-readable reason when state is failed.
    PlatformVolume:
      type: object
      description: >-
        A volume. Responses can include additional fields; ignore fields you do not recognize.
      required:
        - id
        - name
        - status
      properties:
        id:
          type: string
          format: uuid
        source_drive_id:
          type:
            - string
            - "null"
          format: uuid
          description: The volume this one was duplicated from.
        name:
          type: string
        status:
          type: string
          description: active for a usable volume.
        size_bytes:
          type: integer
          format: int64
          description: Measured size. It can lag writes from mounted machines.
        file_count:
          type:
            - integer
            - "null"
        file_tree:
          type: array
          description: A preview of recently updated files. Use List volume files for a complete listing.
          items:
            type: object
        file_tree_status:
          type: string
        content_epoch:
          type: integer
          format: int64
          description: Increases whenever the volume's files change.
        source:
          type: object
          properties:
            status:
              type: string
            region:
              type:
                - string
                - "null"
              description: Storage region.
            read_only:
              type: boolean
            github_repositories:
              type: array
              items:
                $ref: "#/components/schemas/PlatformVolumeGithubRepository"
        file_capabilities:
          type: object
          properties:
            state:
              type: string
            can_upload:
              type: boolean
            message:
              type:
                - string
                - "null"
        active_mount:
          anyOf:
            - $ref: "#/components/schemas/PlatformVolumeMount"
            - type: "null"
        active_mounts:
          type: array
          items:
            $ref: "#/components/schemas/PlatformVolumeMount"
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    PlatformVolumePage:
      type: object
      required:
        - drives
      properties:
        drives:
          type: array
          items:
            $ref: "#/components/schemas/PlatformVolume"
        next_cursor:
          type:
            - string
            - "null"
          description: Pass unchanged as cursor, with the same filters. null means the last page.
    PlatformVolumePlacement:
      type: object
      description: The storage copy prepared next to the reservation's machines.
      properties:
        state:
          type: string
          description: pre_positioning, ready, failed, or stale.
        target_region:
          type: string
        error:
          type:
            - string
            - "null"
        ready_at:
          type:
            - string
            - "null"
          format: date-time
        transfer:
          type:
            - object
            - "null"
          properties:
            state:
              type: string
            objects_total:
              type:
                - integer
                - "null"
            objects_copied:
              type:
                - integer
                - "null"
            bytes_total:
              type:
                - integer
                - "null"
            bytes_copied:
              type:
                - integer
                - "null"
            error:
              type:
                - string
                - "null"
    PlatformVolumeAttachment:
      type: object
      description: A reservation's volume attachment.
      properties:
        id:
          type: string
          format: uuid
        reservation_id:
          type: string
          format: uuid
        drive_id:
          type: string
          format: uuid
        mount_path:
          type: string
        mount_mode:
          type: string
        access_mode:
          type: string
          description: read-write or read-only.
        state:
          type: string
          description: >-
            pre_positioning while Ornn prepares the copy, ready when the copy is in place, failed, or detaching.
            Mounting on each machine follows ready; read the volume's active_mounts for per-machine state.
        error:
          type:
            - string
            - "null"
        created_at:
          type: string
          format: date-time
        drive:
          anyOf:
            - $ref: "#/components/schemas/PlatformVolume"
            - type: "null"
        placement:
          anyOf:
            - $ref: "#/components/schemas/PlatformVolumePlacement"
            - type: "null"
    PlatformVolumeAttachmentEnvelope:
      type: object
      required:
        - attachment
      properties:
        attachment:
          anyOf:
            - $ref: "#/components/schemas/PlatformVolumeAttachment"
            - type: "null"
    PlatformReservationVolume:
      type: object
      description: The volume attached to a reservation, when reservation_id is set.
      required:
        - drives
        - attachment
      properties:
        drives:
          type: array
          description: Zero or one volume.
          items:
            $ref: "#/components/schemas/PlatformVolume"
        attachment:
          anyOf:
            - $ref: "#/components/schemas/PlatformVolumeAttachment"
            - type: "null"
    PlatformCreateVolume:
      type: object
      required:
        - name
      properties:
        name:
          type: string
          description: Volume name.
        source_drive_id:
          type: string
          format: uuid
          description: One of your volumes to duplicate. Its files are copied in the background.
    PlatformVolumeAttachmentChange:
      type: object
      required:
        - reservation_id
      properties:
        reservation_id:
          type: string
          format: uuid
          description: An owned reservation.
        deployed:
          type: boolean
          description: Set true to attach and mount the volume on every machine in the reservation.
        attached:
          type: boolean
          description: Set false to detach the volume from the reservation.
        access_mode:
          type: string
          enum:
            - read-write
            - read-only
          default: read-write
        mount_path:
          type: string
          default: /home/tenant/volume
          description: Absolute path on each machine.
    PlatformVolumeEntry:
      type: object
      properties:
        path:
          type: string
          description: Path relative to the volume root.
        name:
          type: string
        kind:
          type: string
          enum:
            - file
            - directory
        size_bytes:
          type: integer
          format: int64
        updated_at:
          type: string
          format: date-time
    PlatformVolumeContents:
      type: object
      properties:
        path:
          type: string
        search:
          type: string
        entries:
          type: array
          items:
            $ref: "#/components/schemas/PlatformVolumeEntry"
        total:
          type: integer
          description: Entries at this level, or matches on this page for a recursive or search listing.
        next_cursor:
          description: Pass unchanged as cursor. null means the last page.
          oneOf:
            - type: string
            - type: integer
            - type: "null"
    PlatformFileWrite:
      type: object
      properties:
        ok:
          type: boolean
        file:
          type: object
          properties:
            path:
              type: string
            size_bytes:
              type: integer
              format: int64
    PlatformStartUpload:
      type: object
      required:
        - path
        - size_bytes
      properties:
        path:
          type: string
          description: Destination path relative to the volume root.
        size_bytes:
          type: integer
          format: int64
          minimum: 0
          description: Exact size of the file you will upload.
        content_type:
          type: string
          default: application/octet-stream
        sha256:
          type: string
          description: Optional lowercase or uppercase hex SHA-256 of the file, 64 characters.
    PlatformUploadSession:
      type: object
      properties:
        upload_id:
          type: string
          format: uuid
        path:
          type: string
        method:
          type: string
          description: HTTP method for upload_url, currently PUT.
        upload_url:
          type: string
          description: Short-lived URL that accepts the file bytes. Do not send your Ornn API key to it.
        headers:
          type: object
          additionalProperties:
            type: string
          description: Headers to send with the upload.
        expires_in_seconds:
          type: integer
        max_size_bytes:
          type: integer
          format: int64
        state:
          type: string
          description: complete when a retried key already finished.
    PlatformUploadResult:
      type: object
      properties:
        ok:
          type: boolean
        upload_id:
          type: string
          format: uuid
        path:
          type: string
        state:
          type: string
          enum:
            - complete
            - cancelled
    PlatformGithubConnection:
      type: object
      properties:
        id:
          type: string
          format: uuid
        state:
          type: string
        account_login:
          type:
            - string
            - "null"
        account_type:
          type:
            - string
            - "null"
        connected_at:
          type:
            - string
            - "null"
          format: date-time
        installation_url:
          type:
            - string
            - "null"
          description: Manage the app's repository access on GitHub.
    PlatformGithubConnections:
      type: object
      properties:
        configured:
          type: boolean
          description: Whether GitHub pulls are available in this environment.
        connections:
          type: array
          items:
            $ref: "#/components/schemas/PlatformGithubConnection"
    PlatformGithubRepositories:
      type: object
      properties:
        repositories:
          type: array
          items:
            type: object
            properties:
              id:
                type: integer
                format: int64
              full_name:
                type: string
              private:
                type: boolean
              default_branch:
                type:
                  - string
                  - "null"
              archived:
                type: boolean
    PlatformGithubBranches:
      type: object
      properties:
        branches:
          type: array
          items:
            type: object
            properties:
              name:
                type: string
              commit_sha:
                type: string
        has_more:
          type: boolean
    PlatformPullRepository:
      type: object
      required:
        - connection_id
        - repository_id
        - full_name
        - branch
        - folder
      properties:
        connection_id:
          type: string
          format: uuid
        repository_id:
          type: integer
          format: int64
        full_name:
          type: string
          description: Repository owner and name.
        branch:
          type: string
        folder:
          type: string
          description: New folder on the volume. 1 to 64 letters, digits, hyphens, underscores, or periods.
          maxLength: 64
    PlatformNetworkStep:
      type: object
      properties:
        network_id:
          type: string
          format: uuid
        compartment_id:
          type: string
          description: The step, such as provision_key.
        status:
          type: string
          description: pending, running, completed, or failed.
        error:
          type:
            - string
            - "null"
    PlatformNetwork:
      type: object
      required:
        - id
        - name
        - cidr
        - status
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
        cidr:
          type: string
          description: Private IPv4 range of the network.
        status:
          type: string
          description: pending, active, degraded, or deleting. Use the network only when active.
        endpoint:
          type:
            - string
            - "null"
          description: Gateway public address once provisioned.
        public_key:
          type:
            - string
            - "null"
          description: Gateway WireGuard public key once provisioned.
        attached_reservation_count:
          type: integer
          description: Peers connected to the network, including WireGuard configs and machines. Deletion requires 0.
        gateway:
          type: object
          properties:
            status:
              type: string
            endpoint:
              type:
                - string
                - "null"
            public_key:
              type:
                - string
                - "null"
        compartments:
          type: array
          description: Provisioning or teardown steps for the current operation.
          items:
            $ref: "#/components/schemas/PlatformNetworkStep"
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    PlatformNetworkList:
      type: object
      required:
        - networks
      properties:
        networks:
          type: array
          items:
            $ref: "#/components/schemas/PlatformNetwork"
    PlatformNetworkEnvelope:
      type: object
      required:
        - network
      properties:
        network:
          $ref: "#/components/schemas/PlatformNetwork"
    PlatformCreateNetwork:
      type: object
      required:
        - name
      properties:
        name:
          type: string
        cidr:
          type: string
          description: Private IPv4 range from /16 to /24 that overlaps no other network. Omit for an automatic /16.
    PlatformRenameNetwork:
      type: object
      required:
        - name
      properties:
        name:
          type: string
    PlatformCreateWireGuardPeer:
      type: object
      required:
        - public_key
      properties:
        public_key:
          type: string
          description: Your WireGuard public key, 44 characters of base64. Never send the private key.
    PlatformWireGuardPeer:
      type: object
      properties:
        id:
          type: string
          format: uuid
        kind:
          type: string
        public_key:
          type: string
        assigned_ip:
          type: string
          description: Your address inside the private network.
        status:
          type: string
          description: active when the gateway applied it, pending_gateway, failed, or revoked.
        created_at:
          type: string
          format: date-time
        revoked_at:
          type:
            - string
            - "null"
          format: date-time
    PlatformWireGuardNetwork:
      type: object
      properties:
        id:
          type:
            - string
            - "null"
          format: uuid
          description: Network ID, or null for the organization default network.
        cidr:
          type: string
        gateway_ip:
          type: string
        status:
          type: string
        gateway:
          type: object
          properties:
            public_endpoint:
              type: string
            public_key:
              type:
                - string
                - "null"
            udp_port:
              type: integer
            status:
              type: string
    PlatformWireGuardPeerConfig:
      type: object
      properties:
        peer:
          $ref: "#/components/schemas/PlatformWireGuardPeer"
        network:
          $ref: "#/components/schemas/PlatformWireGuardNetwork"
        config:
          type: string
          description: >-
            WireGuard configuration. Replace the PrivateKey placeholder with your private key locally and store the
            file with owner-only permissions.
    PlatformWireGuardPeerList:
      type: object
      properties:
        peers:
          type: array
          items:
            $ref: "#/components/schemas/PlatformWireGuardPeerConfig"
    PlatformTelemetryPoint:
      type: object
      properties:
        observed_at:
          type: string
          format: date-time
        samples:
          type: integer
        values:
          type: object
          description: >-
            Metric values as strings; parse numbers yourself. Includes observed_at, public_ip when reported, the
            customer metrics, and for physical nodes gpus, a JSON-encoded array of per-GPU readings with uuid, index,
            utilization_percent, temperature_celsius, power_watts, memory_used_bytes, memory_total_bytes, and
            fan_speed_percent.
          additionalProperties:
            type: string
    PlatformTelemetryBatchRequest:
      type: object
      properties:
        node_ids:
          type: array
          items:
            type: string
            format: uuid
        virtual_machine_ids:
          type: array
          items:
            type: string
            format: uuid
    PlatformTelemetryBatch:
      type: object
      properties:
        nodes:
          type: array
          items:
            type: object
            properties:
              node_id:
                type: string
                format: uuid
                description: Present for a physical node.
              virtual_machine_id:
                type: string
                format: uuid
                description: Present for a VM.
              observed_at:
                type: string
                format: date-time
              samples:
                type: integer
              values:
                type: object
                additionalProperties:
                  type: string
    PlatformMetricWindow:
      type: object
      properties:
        metric:
          type: string
        start:
          type: string
          format: date-time
        end:
          type: string
          format: date-time
        mean:
          type: number
        median:
          type: number
        std:
          type: number
        min:
          type: number
        max:
          type: number
    PlatformTelemetryWindows:
      type: object
      properties:
        tier:
          type: string
          enum:
            - full
            - hour
            - day
            - month
          description: full returns raw samples, where start equals end. Other tiers summarize each window.
        windows:
          type: array
          items:
            $ref: "#/components/schemas/PlatformMetricWindow"
        gpu_uuid:
          type: string
    PlatformLogPage:
      type: object
      properties:
        stream:
          type: string
        lines:
          type: array
          items:
            type: object
            properties:
              date:
                type: string
                format: date-time
              message:
                type: string
              severity:
                type: string
                description: error, warning, or info.
        next_cursor:
          type:
            - string
            - "null"
    PlatformInvoice:
      type: object
      properties:
        invoiceId:
          type: string
          format: uuid
        reservationId:
          type: string
          format: uuid
        type:
          type: string
          description: >-
            Such as down_payment, recurring for a period invoice, or storage. Handle values you do not
            recognize.
        amountCents:
          type: integer
          format: int64
        currency:
          type: string
        status:
          type: string
          description: >-
            draft, open, paid, void, uncollectible, or a transitional value while the invoice is issued or a payment
            is processed. An open invoice past dueDate is overdue.
        dueDate:
          type: string
          format: date
        paidAt:
          type: string
          format: date-time
        hostedInvoiceUrl:
          type: string
          description: Hosted page for paying the invoice and reading wire instructions.
        invoicePdfUrl:
          type: string
        servicePeriodStart:
          type: string
          format: date
        servicePeriodEnd:
          type: string
          format: date
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
    PlatformInvoicePage:
      type: object
      required:
        - invoices
      properties:
        invoices:
          type: array
          items:
            $ref: "#/components/schemas/PlatformInvoice"
        nextCursor:
          type: string
          description: Pass unchanged as cursor. Absent on the last page.
    PlatformReservationInvoiceList:
      type: object
      properties:
        invoices:
          type: array
          items:
            type: object
            properties:
              invoiceId:
                type: string
                format: uuid
              type:
                type: string
              status:
                type: string
              amountCents:
                type: integer
                format: int64
              currency:
                type: string
              dueDate:
                type:
                  - string
                  - "null"
                format: date-time
              paidAt:
                type:
                  - string
                  - "null"
                format: date-time
              servicePeriodStart:
                type:
                  - string
                  - "null"
                format: date-time
              servicePeriodEnd:
                type:
                  - string
                  - "null"
                format: date-time
              hostedInvoiceUrl:
                type: string
    PlatformCurrencyTotal:
      type: object
      properties:
        currency:
          type: string
        amountCents:
          type: integer
          format: int64
        count:
          type: integer
          description: Number of invoices in the total.
    PlatformBillingSummary:
      type: object
      properties:
        openBalance:
          type: array
          items:
            $ref: "#/components/schemas/PlatformCurrencyTotal"
        earliestDueDate:
          type:
            - string
            - "null"
          format: date-time
        paidThisMonth:
          type: array
          items:
            $ref: "#/components/schemas/PlatformCurrencyTotal"
        paidLastMonth:
          type: array
          items:
            $ref: "#/components/schemas/PlatformCurrencyTotal"
    PlatformUsageReport:
      type: object
      properties:
        windowStart:
          type: string
          format: date
        windowEnd:
          type: string
          format: date
        rows:
          type: array
          items:
            type: object
            properties:
              reservationId:
                type: string
                format: uuid
              status:
                type: string
              gpuCount:
                type: integer
              windowStart:
                type: string
                format: date
              windowEnd:
                type: string
                format: date
              gpuHours:
                type: number
              costCents:
                type: integer
                format: int64
              sharePct:
                type: string
                description: This reservation's share of total cost, as a decimal string.
        totalGpuHours:
          type: number
        totalCostCents:
          type: integer
          format: int64
    PlatformBillingContact:
      type: object
      properties:
        invoiceEmail:
          type:
            - string
            - "null"
        billingAddress:
          type:
            - object
            - "null"
          properties:
            line1:
              type: string
            line2:
              type: string
            city:
              type: string
            region:
              type: string
            postalCode:
              type: string
            country:
              type: string
    PlatformPaymentMethodStatus:
      type: object
      required:
        - saved
      properties:
        saved:
          type: boolean
    PlatformLedgerAccount:
      type: object
      properties:
        organization_id:
          type:
            - string
            - "null"
          format: uuid
        account_role:
          type: string
          description: Such as ORNN_CREDIT. Handle roles you do not recognize.
        currency:
          type: string
    PlatformAccountBalances:
      type: object
      properties:
        accounts:
          type: array
          items:
            type: object
            properties:
              account:
                $ref: "#/components/schemas/PlatformLedgerAccount"
              balance_cents:
                type: string
                description: Signed integer cents as a string. ORNN_CREDIT is negative while credits are available.
    PlatformAccountTransactions:
      type: object
      properties:
        next_cursor:
          type: string
          description: Pass as cursor to read the next page. Empty when there are no more entries.
        transactions:
          type: array
          items:
            type: object
            properties:
              ledger_transaction_id:
                type: string
                format: uuid
              event:
                type: object
                description: >-
                  What caused the transaction. Events recorded for another party contain only event_type,
                  occurred_at, and reservation_id.
                properties:
                  event_type:
                    type: string
                    description: Such as spot_hour_charged, credit_applied, or payment_recorded.
                  source_id:
                    type: string
                  organization_id:
                    type: string
                    format: uuid
                  invoice_id:
                    type:
                      - string
                      - "null"
                    format: uuid
                  payment_intent_id:
                    type: string
                  reservation_id:
                    type:
                      - string
                      - "null"
                    format: uuid
                  occurred_at:
                    type: string
                    format: date-time
                  reverses_transaction_id:
                    type:
                      - string
                      - "null"
                    format: uuid
              recorded_at:
                type: string
                format: date-time
              postings:
                type: array
                description: Your organization's postings only.
                items:
                  type: object
                  properties:
                    account:
                      $ref: "#/components/schemas/PlatformLedgerAccount"
                    amount_cents:
                      type: integer
                      format: int64
  parameters:
    AccountOrganizationId:
      name: organization_id
      in: path
      required: true
      description: Your organization ID, from the organizations array of GET /v1/auth/session.
      schema:
        type: string
        format: uuid
      example: 22222222-2222-4222-8222-222222222222
    AccountReservationId:
      name: reservation_id
      in: path
      required: true
      description: A reservation your organization holds. Keys are delivered to its VM and Bare Metal machines.
      schema:
        type: string
        format: uuid
      example: 11111111-1111-4111-8111-111111111111
    PlatformDriveId:
      name: drive_id
      in: path
      required: true
      description: Volume ID.
      schema:
        type: string
        format: uuid
      example: 77777777-7777-4777-8777-777777777777
    PlatformFilePath:
      name: file_path
      in: path
      required: true
      description: File path relative to the volume root. Keep / between folder names and encode each name.
      schema:
        type: string
      example: results/summary.csv
    PlatformGithubConnectionId:
      name: connection_id
      in: path
      required: true
      description: GitHub connection ID from List GitHub connections.
      schema:
        type: string
        format: uuid
      example: dddddddd-dddd-4ddd-8ddd-dddddddddddd
    PlatformNetworkId:
      name: network_id
      in: path
      required: true
      description: Private network ID.
      schema:
        type: string
        format: uuid
      example: 88888888-8888-4888-8888-888888888888
    PlatformPeerId:
      name: peer_id
      in: path
      required: true
      description: WireGuard config ID (peer.id).
      schema:
        type: string
        format: uuid
      example: 99999999-9999-4999-8999-999999999999
    PlatformNodeId:
      name: node_id
      in: path
      required: true
      description: A physical node ID from List reserved GPU nodes.
      schema:
        type: string
        format: uuid
      example: 33333333-3333-4333-8333-333333333333
    PlatformVirtualMachineId:
      name: virtual_machine_id
      in: path
      required: true
      description: A VM ID in one of your reservations.
      schema:
        type: string
        format: uuid
      example: 66666666-6666-4666-8666-666666666666
    PlatformLogStream:
      name: stream
      in: path
      required: true
      description: Console stream.
      schema:
        type: string
        enum:
          - kernel
          - serial
      example: kernel
    PlatformLatestLimit:
      name: limit
      in: query
      required: false
      description: Number of newest samples, from 1 to 1,000. Defaults to 1.
      schema:
        type: integer
        minimum: 1
        maximum: 1000
        default: 1
    PlatformRangeStart:
      name: start
      in: query
      required: true
      description: Range start, RFC 3339.
      schema:
        type: string
        format: date-time
      example: "2026-10-14T11:00:00Z"
    PlatformRangeEnd:
      name: end
      in: query
      required: true
      description: Range end, RFC 3339. An end at or before start returns no data.
      schema:
        type: string
        format: date-time
      example: "2026-10-14T12:00:00Z"
    PlatformMaxPoints:
      name: max_points
      in: query
      required: false
      description: Target number of points per metric, from 1 to 500. Defaults to 240.
      schema:
        type: integer
        minimum: 1
        maximum: 500
        default: 240
    PlatformMetrics:
      name: metrics
      in: query
      required: false
      description: >-
        Comma-separated metric names. Omit for every customer metric. Accepted names are cpu_utilization,
        cpu_temperature, gpu_utilization, gpu_temperature, gpu_power_w, gpu_fan_speed_percent,
        component_temperature_max, memory_utilization, gpu_memory_utilization, storage_utilization, and
        ethernet_latency_ms; other names are ignored.
      schema:
        type: string
      example: gpu_utilization,gpu_temperature
    PlatformLogLimit:
      name: limit
      in: query
      required: false
      description: Lines per page, from 1 to 1,000. Defaults to 200.
      schema:
        type: integer
        minimum: 1
        maximum: 1000
        default: 200
    PlatformLogCursor:
      name: cursor
      in: query
      required: false
      description: next_cursor from the previous page, sent with the same start, end, and order.
      schema:
        type: string
    PlatformLogOrder:
      name: order
      in: query
      required: false
      description: desc returns the newest page first; asc walks oldest to newest.
      schema:
        type: string
        enum:
          - desc
          - asc
        default: desc
externalDocs:
  description: REST integration walkthrough
  url: https://docs.ornn.com/guides/kubernetes-access#provision-and-manage-through-rest
