{
  "openapi": "3.1.0",
  "info": {
    "title": "Racko Public VPS API",
    "version": "2026-03-11",
    "description": "OAuth2 client-credentials access to VPS operations under `/api/v1/public/*`.\nCreate credentials in the Racko portal (platform or tenant admin → API Credentials),\nobtain a bearer token, then call the public routes with scoped access.\n\n**Rate limiting:** each credential is limited (default **120 requests/minute**; optional per-credential override).\nWhen exceeded, the API returns **429** with a **Retry-After** header (seconds).\n\n**Owner types:** tokens are issued for either a **platform** admin or a **tenant** admin pool.\nVM list/detail responses use the same **`PublicVm`** contract for both owner types (no internal Mongo fields or stored console credentials).\n**VM creation** (`POST /api/v1/public/vms`) is supported for **platform** credentials only.\n"
  },
  "servers": [
    {
      "url": "{gatewayBaseUrl}",
      "description": "Racko API gateway (no trailing slash)",
      "variables": {
        "gatewayBaseUrl": {
          "default": "http://localhost:8000",
          "description": "Production/staging gateway URL"
        }
      }
    }
  ],
  "tags": [
    {
      "name": "OAuth",
      "description": "Token issuance (no bearer required)"
    },
    {
      "name": "Public VPS",
      "description": "Bearer access token (audience `racko-public-api`)"
    }
  ],
  "paths": {
    "/api/v1/oauth/token": {
      "post": {
        "tags": [
          "OAuth"
        ],
        "operationId": "oauthToken",
        "summary": "Obtain an access token (client credentials)",
        "description": "Accepts `grant_type=client_credentials` only.\n\n**Client authentication** (either):\n- HTTP **Basic** `Authorization: Basic base64(client_id:client_secret)` (preferred when present), or\n- Form fields `client_id` and `client_secret` in the body together with `grant_type`.\n\nRepeated invalid authentication may be throttled (same response as `invalid_client`).\n",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/OAuthTokenRequest"
              },
              "examples": {
                "formBody": {
                  "value": {
                    "grant_type": "client_credentials",
                    "client_id": "rk_live_example",
                    "client_secret": "your_secret"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Access token issued",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OAuthTokenResponse"
                }
              }
            }
          },
          "400": {
            "description": "OAuth error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OAuthError"
                },
                "examples": {
                  "invalid_request": {
                    "value": {
                      "error": "invalid_request",
                      "error_description": "grant_type is required."
                    }
                  },
                  "unsupported_grant_type": {
                    "value": {
                      "error": "unsupported_grant_type",
                      "error_description": "Only grant_type client_credentials is supported."
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "OAuth error (invalid or throttled client)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OAuthError"
                },
                "examples": {
                  "invalid_client": {
                    "value": {
                      "error": "invalid_client",
                      "error_description": "Client authentication failed."
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/public/templates": {
      "get": {
        "tags": [
          "Public VPS"
        ],
        "operationId": "listTemplates",
        "summary": "List VM templates",
        "security": [
          {
            "rackoPublicApi": [
              "vms:read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/PublicSuccessEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "object",
                          "properties": {
                            "templates": {
                              "type": "array",
                              "items": {
                                "$ref": "#/components/schemas/PublicVmTemplate"
                              }
                            },
                            "total": {
                              "type": "integer"
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/InvalidToken"
          },
          "403": {
            "$ref": "#/components/responses/InsufficientScope"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/public/jobs/{id}": {
      "get": {
        "tags": [
          "Public VPS"
        ],
        "operationId": "getJob",
        "summary": "Bulk job status",
        "security": [
          {
            "rackoPublicApi": [
              "vms:read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/PublicResourceId"
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/PublicSuccessEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/PublicJobStatus"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/InvalidToken"
          },
          "403": {
            "$ref": "#/components/responses/InsufficientScope"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/public/vms/assign": {
      "post": {
        "tags": [
          "Public VPS"
        ],
        "operationId": "assignVms",
        "summary": "Assign VMs to user(s)",
        "description": "**Platform** credentials: body matches `PlatformAssignRequest` — provide **exactly one** of\n`userId`, `userEmail`, or `username` (managed user in your admin pool) plus `vmIds`.\n\n**Tenant** credentials: body matches `TenantOnboardRequest` (create/onboard tenant users 1:1 with VMs).\n",
        "security": [
          {
            "rackoPublicApi": [
              "vms:assign"
            ]
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "oneOf": [
                  {
                    "$ref": "#/components/schemas/PlatformAssignRequest"
                  },
                  {
                    "$ref": "#/components/schemas/TenantOnboardRequest"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Platform assign success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicSuccessEnvelope"
                }
              }
            }
          },
          "201": {
            "description": "Tenant onboard success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicSuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "401": {
            "$ref": "#/components/responses/InvalidToken"
          },
          "403": {
            "$ref": "#/components/responses/InsufficientScope"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/public/vms": {
      "get": {
        "tags": [
          "Public VPS"
        ],
        "operationId": "listVms",
        "summary": "List VMs",
        "security": [
          {
            "rackoPublicApi": [
              "vms:read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "creating",
                "running",
                "stopped",
                "paused",
                "suspended",
                "error",
                "deleting",
                "delete_failed"
              ]
            }
          },
          {
            "name": "cloneType",
            "in": "query",
            "description": "Platform credentials only (ignored for tenant list filtering semantics)",
            "schema": {
              "type": "string",
              "enum": [
                "dedicated_storage",
                "dynamic_storage"
              ]
            }
          },
          {
            "name": "node",
            "in": "query",
            "schema": {
              "type": "string",
              "maxLength": 63,
              "pattern": "^[a-zA-Z0-9-]+$"
            }
          },
          {
            "name": "projectId",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/MongoObjectId"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/PublicSuccessEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "object",
                          "properties": {
                            "vms": {
                              "type": "array",
                              "items": {
                                "$ref": "#/components/schemas/PublicVm"
                              }
                            },
                            "total": {
                              "type": "integer"
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/InvalidToken"
          },
          "403": {
            "$ref": "#/components/responses/InsufficientScope"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "post": {
        "tags": [
          "Public VPS"
        ],
        "operationId": "createVms",
        "summary": "Create VM(s) (async job)",
        "description": "Platform credentials only. Returns **202** with `jobId` in `data`.",
        "security": [
          {
            "rackoPublicApi": [
              "vms:write"
            ]
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateVmRequest"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Creation job started",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/PublicSuccessEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "object",
                          "required": [
                            "jobId"
                          ],
                          "properties": {
                            "jobId": {
                              "type": "string"
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "401": {
            "$ref": "#/components/responses/InvalidToken"
          },
          "403": {
            "$ref": "#/components/responses/InsufficientScope"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/public/vms/{id}": {
      "get": {
        "tags": [
          "Public VPS"
        ],
        "operationId": "getVm",
        "summary": "Get VM details",
        "security": [
          {
            "rackoPublicApi": [
              "vms:read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/PublicResourceId"
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/PublicSuccessEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/PublicVmDetail"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/InvalidToken"
          },
          "403": {
            "$ref": "#/components/responses/InsufficientScope"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "delete": {
        "tags": [
          "Public VPS"
        ],
        "operationId": "deleteVm",
        "summary": "Delete VM",
        "security": [
          {
            "rackoPublicApi": [
              "vms:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/PublicResourceId"
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/PublicSuccessEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "object",
                          "properties": {
                            "deleted": {
                              "type": "boolean",
                              "const": true
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/InvalidToken"
          },
          "403": {
            "$ref": "#/components/responses/InsufficientScope"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/public/vms/{id}/start": {
      "post": {
        "tags": [
          "Public VPS"
        ],
        "operationId": "startVm",
        "summary": "Start VM",
        "description": "Validates the request and starts the power operation asynchronously.\nReturns **202 Accepted** immediately; poll `GET /api/v1/public/vms/{id}/status` for completion.\n",
        "security": [
          {
            "rackoPublicApi": [
              "vms:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/PublicResourceId"
          }
        ],
        "responses": {
          "202": {
            "description": "Power operation accepted (in progress)",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/PublicSuccessEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/PublicPowerActionAccepted"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/InvalidToken"
          },
          "403": {
            "$ref": "#/components/responses/InsufficientScope"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/public/vms/{id}/stop": {
      "post": {
        "tags": [
          "Public VPS"
        ],
        "operationId": "stopVm",
        "summary": "Stop VM",
        "description": "Validates the request and stops the VM asynchronously.\nReturns **202 Accepted** immediately; poll `GET /api/v1/public/vms/{id}/status` for completion.\n",
        "security": [
          {
            "rackoPublicApi": [
              "vms:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/PublicResourceId"
          }
        ],
        "responses": {
          "202": {
            "description": "Power operation accepted (in progress)",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/PublicSuccessEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/PublicPowerActionAccepted"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/InvalidToken"
          },
          "403": {
            "$ref": "#/components/responses/InsufficientScope"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/public/vms/{id}/restart": {
      "post": {
        "tags": [
          "Public VPS"
        ],
        "operationId": "restartVm",
        "summary": "Restart VM",
        "description": "Validates the request and restarts the VM asynchronously.\nReturns **202 Accepted** immediately; poll `GET /api/v1/public/vms/{id}/status` for completion.\n",
        "security": [
          {
            "rackoPublicApi": [
              "vms:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/PublicResourceId"
          }
        ],
        "responses": {
          "202": {
            "description": "Power operation accepted (in progress)",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/PublicSuccessEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/PublicPowerActionAccepted"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/InvalidToken"
          },
          "403": {
            "$ref": "#/components/responses/InsufficientScope"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/public/vms/{id}/status": {
      "get": {
        "tags": [
          "Public VPS"
        ],
        "operationId": "getVmStatus",
        "summary": "VM status",
        "security": [
          {
            "rackoPublicApi": [
              "vms:read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/PublicResourceId"
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/PublicSuccessEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "object",
                          "required": [
                            "status"
                          ],
                          "properties": {
                            "status": {
                              "$ref": "#/components/schemas/PublicVmLiveStatus"
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/InvalidToken"
          },
          "403": {
            "$ref": "#/components/responses/InsufficientScope"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/public/vms/{id}/console": {
      "get": {
        "tags": [
          "Public VPS"
        ],
        "operationId": "getVmConsole",
        "summary": "Console / Guacamole session URL",
        "security": [
          {
            "rackoPublicApi": [
              "vms:console"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/PublicResourceId"
          },
          {
            "name": "protocol",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "rdp",
                "ssh",
                "vnc"
              ]
            }
          },
          {
            "name": "width",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Optional viewport width (positive integer as string)"
          },
          {
            "name": "height",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Optional viewport height (positive integer as string)"
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/PublicSuccessEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/PublicVmConsoleSession"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/InvalidToken"
          },
          "403": {
            "$ref": "#/components/responses/InsufficientScope"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "rackoPublicApi": {
        "type": "oauth2",
        "description": "Use the access token from `POST /api/v1/oauth/token` as\n`Authorization: Bearer <access_token>`. Token lifetime is **3600 seconds (1 hour)**.\nJWT claims include `typ: api_access`, `aud: racko-public-api`, and a space-delimited `scope` string.\n",
        "flows": {
          "clientCredentials": {
            "tokenUrl": "/api/v1/oauth/token",
            "scopes": {
              "vms:read": "Read VMs, templates, jobs, and status",
              "vms:write": "Create, delete, and power-manage VMs",
              "vms:console": "Open console / Guacamole URLs",
              "vms:assign": "Assign VMs to users (platform or tenant onboard)"
            }
          }
        }
      }
    },
    "parameters": {
      "PublicResourceId": {
        "name": "id",
        "in": "path",
        "required": true,
        "description": "MongoDB ObjectId (24 hex characters)",
        "schema": {
          "$ref": "#/components/schemas/MongoObjectId"
        }
      }
    },
    "schemas": {
      "MongoObjectId": {
        "type": "string",
        "pattern": "^[a-fA-F0-9]{24}$",
        "example": "507f1f77bcf86cd799439011"
      },
      "PublicSuccessEnvelope": {
        "type": "object",
        "required": [
          "api_version",
          "data"
        ],
        "properties": {
          "api_version": {
            "type": "string",
            "example": "2026-03-11"
          },
          "data": {
            "type": "object",
            "description": "Endpoint-specific payload"
          }
        }
      },
      "PublicError": {
        "type": "object",
        "required": [
          "error",
          "error_description"
        ],
        "properties": {
          "error": {
            "type": "string",
            "enum": [
              "invalid_token",
              "insufficient_scope",
              "not_found",
              "invalid_request",
              "rate_limited",
              "server_error"
            ]
          },
          "error_description": {
            "type": "string"
          }
        }
      },
      "OAuthError": {
        "type": "object",
        "required": [
          "error",
          "error_description"
        ],
        "properties": {
          "error": {
            "type": "string",
            "enum": [
              "invalid_request",
              "invalid_client",
              "unsupported_grant_type"
            ]
          },
          "error_description": {
            "type": "string"
          }
        }
      },
      "OAuthTokenRequest": {
        "type": "object",
        "required": [
          "grant_type"
        ],
        "properties": {
          "grant_type": {
            "type": "string",
            "enum": [
              "client_credentials"
            ]
          },
          "client_id": {
            "type": "string",
            "description": "Required in body when not using HTTP Basic"
          },
          "client_secret": {
            "type": "string",
            "description": "Required in body when not using HTTP Basic"
          }
        }
      },
      "OAuthTokenResponse": {
        "type": "object",
        "required": [
          "access_token",
          "token_type",
          "expires_in",
          "scope"
        ],
        "properties": {
          "access_token": {
            "type": "string"
          },
          "token_type": {
            "type": "string",
            "enum": [
              "Bearer"
            ]
          },
          "expires_in": {
            "type": "integer",
            "example": 3600
          },
          "scope": {
            "type": "string",
            "description": "Space-separated granted scopes",
            "example": "vms:read vms:write"
          }
        }
      },
      "CreateVmRequest": {
        "type": "object",
        "required": [
          "templateId",
          "name",
          "count",
          "cloneType",
          "passwordMode"
        ],
        "properties": {
          "templateId": {
            "type": "integer",
            "minimum": 1
          },
          "name": {
            "type": "string",
            "minLength": 3,
            "maxLength": 50,
            "pattern": "^[a-zA-Z0-9-]+$"
          },
          "count": {
            "type": "integer",
            "minimum": 1,
            "maximum": 100
          },
          "cloneType": {
            "type": "string",
            "enum": [
              "dedicated_storage",
              "dynamic_storage"
            ]
          },
          "cpuCores": {
            "type": "integer",
            "minimum": 1,
            "maximum": 128
          },
          "memoryGb": {
            "type": "number",
            "minimum": 0.5,
            "maximum": 512
          },
          "diskGb": {
            "type": "integer",
            "minimum": 10,
            "maximum": 10000
          },
          "description": {
            "type": "string",
            "maxLength": 500
          },
          "passwordMode": {
            "type": "string",
            "enum": [
              "fixed",
              "dynamic"
            ]
          },
          "consolePassword": {
            "type": "string",
            "maxLength": 256,
            "description": "Required when passwordMode is fixed"
          },
          "enableVirtualization": {
            "type": "boolean",
            "default": false
          },
          "softwareIds": {
            "type": "array",
            "maxItems": 20,
            "items": {
              "$ref": "#/components/schemas/MongoObjectId"
            }
          },
          "projectId": {
            "$ref": "#/components/schemas/MongoObjectId"
          },
          "networkType": {
            "type": "string",
            "enum": [
              "public",
              "private"
            ],
            "default": "public"
          }
        }
      },
      "PlatformAssignRequest": {
        "type": "object",
        "required": [
          "vmIds"
        ],
        "description": "Provide **exactly one** assignee identifier (`userId`, `userEmail`, or `username`).\nThe user must belong to your platform admin pool (users you created).\n",
        "properties": {
          "userId": {
            "$ref": "#/components/schemas/MongoObjectId"
          },
          "userEmail": {
            "type": "string",
            "format": "email"
          },
          "username": {
            "type": "string",
            "minLength": 1,
            "maxLength": 128
          },
          "vmIds": {
            "type": "array",
            "minItems": 1,
            "maxItems": 50,
            "items": {
              "$ref": "#/components/schemas/MongoObjectId"
            }
          },
          "accessSchedule": {
            "$ref": "#/components/schemas/AccessScheduleInput"
          }
        }
      },
      "PublicVmTemplate": {
        "type": "object",
        "required": [
          "templateId",
          "name",
          "defaultCpuCores",
          "defaultMemoryGb",
          "defaultDiskGb",
          "isCustom"
        ],
        "description": "`templateId` is the Proxmox template vmid — pass it as `templateId` in `POST /api/v1/public/vms`.\n",
        "properties": {
          "templateId": {
            "type": "integer",
            "minimum": 1
          },
          "name": {
            "type": "string"
          },
          "defaultCpuCores": {
            "type": "number"
          },
          "defaultMemoryGb": {
            "type": "number"
          },
          "defaultDiskGb": {
            "type": "integer"
          },
          "isCustom": {
            "type": "boolean"
          }
        }
      },
      "TenantOnboardRequest": {
        "type": "object",
        "required": [
          "vmIds",
          "passwordMode"
        ],
        "properties": {
          "vmIds": {
            "type": "array",
            "minItems": 1,
            "maxItems": 50,
            "items": {
              "$ref": "#/components/schemas/MongoObjectId"
            }
          },
          "emailPrefix": {
            "type": "string",
            "format": "email"
          },
          "passwordMode": {
            "type": "string",
            "enum": [
              "auto",
              "shared"
            ]
          },
          "sharedPassword": {
            "type": "string",
            "minLength": 8,
            "maxLength": 128,
            "description": "Required when passwordMode is shared"
          },
          "email": {
            "type": "string",
            "format": "email",
            "description": "Allowed only when vmIds.length is 1"
          },
          "accessSchedule": {
            "$ref": "#/components/schemas/AccessScheduleInput"
          }
        }
      },
      "AccessScheduleInput": {
        "type": "object",
        "properties": {
          "startDate": {
            "type": "string",
            "nullable": true
          },
          "endDate": {
            "type": "string",
            "nullable": true
          },
          "startTime": {
            "type": "string",
            "nullable": true
          },
          "endTime": {
            "type": "string",
            "nullable": true
          },
          "weeklySchedule": {
            "type": "array",
            "nullable": true,
            "items": {}
          },
          "timezone": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "PublicVmAssignment": {
        "type": "object",
        "required": [
          "assigneeId"
        ],
        "properties": {
          "assigneeId": {
            "$ref": "#/components/schemas/MongoObjectId"
          },
          "email": {
            "type": "string",
            "format": "email"
          },
          "isActive": {
            "type": "boolean"
          }
        }
      },
      "PublicVmAccessSchedule": {
        "type": "object",
        "required": [
          "override",
          "timezone"
        ],
        "properties": {
          "startDate": {
            "type": "string",
            "format": "date",
            "nullable": true
          },
          "endDate": {
            "type": "string",
            "format": "date",
            "nullable": true
          },
          "startTime": {
            "type": "string",
            "nullable": true
          },
          "endTime": {
            "type": "string",
            "nullable": true
          },
          "override": {
            "type": "boolean"
          },
          "overrideUntil": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "timezone": {
            "type": "string"
          },
          "weeklySchedule": {
            "type": "array",
            "nullable": true,
            "items": {
              "type": "object",
              "additionalProperties": true
            }
          }
        }
      },
      "PublicVm": {
        "type": "object",
        "required": [
          "id",
          "name",
          "status",
          "allocatedCpu",
          "allocatedMemoryGb",
          "allocatedDiskGb",
          "consoleProtocol",
          "consoleReady",
          "createdAt",
          "updatedAt"
        ],
        "properties": {
          "id": {
            "$ref": "#/components/schemas/MongoObjectId"
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "proxmoxStatus": {
            "type": "string"
          },
          "ipAddress": {
            "type": "string"
          },
          "networkType": {
            "type": "string",
            "enum": [
              "public",
              "private"
            ]
          },
          "cloneType": {
            "type": "string",
            "enum": [
              "dedicated_storage",
              "dynamic_storage"
            ]
          },
          "allocatedCpu": {
            "type": "number"
          },
          "allocatedMemoryGb": {
            "type": "number"
          },
          "allocatedDiskGb": {
            "type": "number"
          },
          "consoleProtocol": {
            "type": "string",
            "enum": [
              "rdp",
              "ssh"
            ]
          },
          "consoleReady": {
            "type": "boolean"
          },
          "planStatus": {
            "type": "string",
            "enum": [
              "active",
              "expired"
            ],
            "nullable": true
          },
          "planPeriodEnd": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "billingPeriod": {
            "type": "string",
            "enum": [
              "monthly",
              "quarterly",
              "yearly"
            ],
            "nullable": true
          },
          "assignment": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/PublicVmAssignment"
              },
              {
                "type": "null"
              }
            ]
          },
          "accessSchedule": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/PublicVmAccessSchedule"
              },
              {
                "type": "null"
              }
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "PublicVmLiveStatus": {
        "type": "object",
        "required": [
          "status"
        ],
        "properties": {
          "status": {
            "type": "string"
          },
          "cpu": {
            "type": "object",
            "properties": {
              "usagePercent": {
                "type": "number"
              },
              "allocated": {
                "type": "number"
              }
            }
          },
          "memory": {
            "type": "object",
            "properties": {
              "usedGb": {
                "type": "number"
              },
              "allocatedGb": {
                "type": "number"
              },
              "usagePercent": {
                "type": "number"
              }
            }
          },
          "disk": {
            "type": "object",
            "properties": {
              "usedGb": {
                "type": "number"
              },
              "allocatedGb": {
                "type": "number"
              }
            }
          },
          "uptime": {
            "type": "object",
            "properties": {
              "seconds": {
                "type": "integer"
              },
              "formatted": {
                "type": "string"
              }
            }
          },
          "ipAddress": {
            "type": "string"
          }
        }
      },
      "PublicVmDetail": {
        "type": "object",
        "required": [
          "vm"
        ],
        "properties": {
          "vm": {
            "$ref": "#/components/schemas/PublicVm"
          },
          "liveStatus": {
            "$ref": "#/components/schemas/PublicVmLiveStatus"
          }
        }
      },
      "PublicJobVmSummary": {
        "type": "object",
        "required": [
          "id",
          "name",
          "status",
          "consoleProtocol"
        ],
        "properties": {
          "id": {
            "$ref": "#/components/schemas/MongoObjectId"
          },
          "name": {
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "ipAddress": {
            "type": "string"
          },
          "consoleProtocol": {
            "type": "string",
            "enum": [
              "rdp",
              "ssh"
            ]
          }
        }
      },
      "PublicJobStatus": {
        "type": "object",
        "required": [
          "job",
          "vms"
        ],
        "properties": {
          "job": {
            "type": "object",
            "required": [
              "id",
              "type",
              "status",
              "total",
              "completed",
              "failed",
              "pending",
              "vmIds",
              "failedVmids",
              "jobErrors",
              "startedAt",
              "createdAt",
              "updatedAt"
            ],
            "properties": {
              "id": {
                "$ref": "#/components/schemas/MongoObjectId"
              },
              "type": {
                "type": "string"
              },
              "status": {
                "type": "string"
              },
              "total": {
                "type": "integer"
              },
              "completed": {
                "type": "integer"
              },
              "failed": {
                "type": "integer"
              },
              "pending": {
                "type": "integer"
              },
              "vmIds": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/MongoObjectId"
                }
              },
              "failedVmids": {
                "type": "array",
                "items": {
                  "type": "integer"
                }
              },
              "jobErrors": {
                "type": "array",
                "items": {
                  "type": "object",
                  "required": [
                    "index",
                    "vmName",
                    "error"
                  ],
                  "properties": {
                    "index": {
                      "type": "integer"
                    },
                    "vmName": {
                      "type": "string"
                    },
                    "error": {
                      "type": "string"
                    }
                  }
                }
              },
              "startedAt": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              },
              "completedAt": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              },
              "cancelledAt": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              },
              "createdAt": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              },
              "updatedAt": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              }
            }
          },
          "vms": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PublicJobVmSummary"
            }
          }
        }
      },
      "PublicVmConsoleSession": {
        "type": "object",
        "required": [
          "protocol",
          "clientUrl",
          "connectionId"
        ],
        "properties": {
          "protocol": {
            "type": "string"
          },
          "clientUrl": {
            "type": "string",
            "format": "uri"
          },
          "connectionId": {
            "type": "string"
          }
        }
      },
      "PublicPowerActionAccepted": {
        "type": "object",
        "required": [
          "status",
          "vmId",
          "operation"
        ],
        "properties": {
          "status": {
            "type": "string",
            "const": "accepted"
          },
          "vmId": {
            "$ref": "#/components/schemas/MongoObjectId"
          },
          "operation": {
            "type": "string",
            "enum": [
              "start",
              "stop",
              "restart"
            ]
          }
        }
      }
    },
    "responses": {
      "InvalidToken": {
        "description": "Missing, malformed, or expired bearer token",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/PublicError"
            },
            "example": {
              "error": "invalid_token",
              "error_description": "Invalid or expired access token."
            }
          }
        }
      },
      "InsufficientScope": {
        "description": "Token valid but missing required scope",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/PublicError"
            },
            "example": {
              "error": "insufficient_scope",
              "error_description": "The request requires the vms:write scope."
            }
          }
        }
      },
      "NotFound": {
        "description": "Resource not found or not accessible in owner scope",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/PublicError"
            },
            "example": {
              "error": "not_found",
              "error_description": "VM not found."
            }
          }
        }
      },
      "InvalidRequest": {
        "description": "Validation or business rule failure",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/PublicError"
            },
            "example": {
              "error": "invalid_request",
              "error_description": "Validation failed."
            }
          }
        }
      },
      "RateLimited": {
        "description": "Per-credential rate limit exceeded",
        "headers": {
          "Retry-After": {
            "description": "Seconds until the current window resets",
            "schema": {
              "type": "integer",
              "example": 42
            }
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/PublicError"
            },
            "example": {
              "error": "rate_limited",
              "error_description": "Rate limit exceeded (120 requests per minute). Retry after 42 seconds."
            }
          }
        }
      }
    }
  }
}