{
  "openapi": "3.1.0",
  "info": {
    "title": "DataPasa Cloud API",
    "version": "1.0.0",
    "summary": "Hourly billed VPS and game server hosting",
    "description": "API behind the DataPasa control panel at https://cloud.datapasa.com. Public endpoints under /api/public need no credentials. Everything else expects a bearer access token obtained from /api/auth/login. All prices are in USD. See https://datapasa.com/auth.md for how to obtain credentials and https://datapasa.com/docs/api.md for a short guide.",
    "termsOfService": "https://datapasa.com/en/terms",
    "contact": {
      "name": "DataPasa support",
      "email": "support@datapasa.com",
      "url": "https://datapasa.com/en/contacts"
    }
  },
  "servers": [
    { "url": "https://cloud.datapasa.com" }
  ],
  "tags": [
    { "name": "public", "description": "Open endpoints, no credentials" },
    { "name": "auth", "description": "Account registration and tokens" },
    { "name": "account" },
    { "name": "cloud", "description": "KVM virtual servers" },
    { "name": "game", "description": "Game servers on the Pterodactyl panel" },
    { "name": "billing", "description": "Prepaid balance and top ups" }
  ],
  "paths": {
    "/api/public/catalog": {
      "get": {
        "tags": ["public"],
        "operationId": "getPublicCatalog",
        "summary": "VPS locations, plans, prices and referral terms",
        "security": [],
        "responses": {
          "200": {
            "description": "Catalog, cached for 300 seconds",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/PublicCatalogResponse" }
              }
            }
          }
        }
      }
    },
    "/api/public/games": {
      "get": {
        "tags": ["public"],
        "operationId": "getPublicGames",
        "summary": "Game server catalog with plans and prices",
        "security": [],
        "responses": {
          "200": {
            "description": "Games, cached for 300 seconds",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/PublicGamesResponse" }
              }
            }
          }
        }
      }
    },
    "/api/auth/register": {
      "post": {
        "tags": ["auth"],
        "operationId": "register",
        "summary": "Create an account",
        "description": "Creates a customer account and returns tokens. A verification email is sent; ordering requires a verified email. Limited to 10 requests per hour per IP address.",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/RegisterRequest" }
            }
          }
        },
        "responses": {
          "201": { "$ref": "#/components/responses/Tokens" },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "409": { "$ref": "#/components/responses/Conflict" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/api/auth/login": {
      "post": {
        "tags": ["auth"],
        "operationId": "login",
        "summary": "Exchange email and password for tokens",
        "description": "Limited to 10 requests per 15 minutes per email and IP address.",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/LoginRequest" }
            }
          }
        },
        "responses": {
          "200": { "$ref": "#/components/responses/Tokens" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/api/auth/refresh": {
      "post": {
        "tags": ["auth"],
        "operationId": "refresh",
        "summary": "Rotate the refresh token and get a new access token",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/RefreshRequest" }
            }
          }
        },
        "responses": {
          "200": { "$ref": "#/components/responses/Tokens" },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    },
    "/api/auth/logout": {
      "post": {
        "tags": ["auth"],
        "operationId": "logout",
        "summary": "Revoke a refresh token",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/RefreshRequest" }
            }
          }
        },
        "responses": {
          "204": { "description": "Revoked" }
        }
      }
    },
    "/api/auth/verify-email": {
      "post": {
        "tags": ["auth"],
        "operationId": "verifyEmail",
        "summary": "Confirm an email address with the token from the verification email",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/TokenRequest" }
            }
          }
        },
        "responses": {
          "204": { "description": "Verified" },
          "400": { "$ref": "#/components/responses/BadRequest" }
        }
      }
    },
    "/api/auth/password/forgot": {
      "post": {
        "tags": ["auth"],
        "operationId": "forgotPassword",
        "summary": "Send a password reset email",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/EmailRequest" }
            }
          }
        },
        "responses": {
          "204": { "description": "Accepted" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/api/auth/password/reset": {
      "post": {
        "tags": ["auth"],
        "operationId": "resetPassword",
        "summary": "Set a new password with the token from the reset email",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/ResetPasswordRequest" }
            }
          }
        },
        "responses": {
          "204": { "description": "Password changed" },
          "400": { "$ref": "#/components/responses/BadRequest" }
        }
      }
    },
    "/api/me": {
      "get": {
        "tags": ["account"],
        "operationId": "getMe",
        "summary": "Current account",
        "description": "Reports emailVerified and fingerprintRequired. While fingerprintRequired is true every other authenticated endpoint answers 403 FINGERPRINT_REQUIRED until the customer completes the device check in the control panel.",
        "responses": {
          "200": {
            "description": "Account",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/MeResponse" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    },
    "/api/me/resend-verification": {
      "post": {
        "tags": ["account"],
        "operationId": "resendVerification",
        "summary": "Send the verification email again",
        "responses": {
          "200": {
            "description": "Sent, with the cooldown before the next attempt",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "retryAfterSeconds": { "type": "integer" }
                  }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "409": { "$ref": "#/components/responses/Conflict" }
        }
      }
    },
    "/api/balance": {
      "get": {
        "tags": ["billing"],
        "operationId": "getBalance",
        "summary": "Prepaid balance",
        "responses": {
          "200": {
            "description": "Balance in USD",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/BalanceResponse" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" }
        }
      }
    },
    "/api/payments/methods": {
      "get": {
        "tags": ["billing"],
        "operationId": "getPaymentMethods",
        "summary": "Payment methods available to this account",
        "responses": {
          "200": {
            "description": "Methods",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": { "$ref": "#/components/schemas/PaymentMethod" }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" }
        }
      }
    },
    "/api/payments/topup": {
      "post": {
        "tags": ["billing"],
        "operationId": "createTopUp",
        "summary": "Create a top up invoice and get the hosted payment page",
        "description": "Minimum amount 5 USD. The customer completes the payment on paymentUrl in a browser.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/TopUpRequest" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Invoice",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/TopUpResponse" }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/api/payments/{invoiceId}/status": {
      "get": {
        "tags": ["billing"],
        "operationId": "getInvoiceStatus",
        "summary": "Status of a top up invoice",
        "parameters": [
          { "name": "invoiceId", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" } }
        ],
        "responses": {
          "200": {
            "description": "Status",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/InvoiceStatusResponse" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      }
    },
    "/api/cloud/order-form": {
      "get": {
        "tags": ["cloud"],
        "operationId": "getCloudOrderForm",
        "summary": "Locations, plans with account specific prices and OS images",
        "responses": {
          "200": {
            "description": "Order form",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/OrderFormResponse" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" }
        }
      }
    },
    "/api/cloud/order": {
      "post": {
        "tags": ["cloud"],
        "operationId": "orderCloudServer",
        "summary": "Create a virtual server",
        "description": "Requires a verified email and enough balance for the first hour. Billing runs hourly until the server is deleted.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/OrderRequest" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Server being provisioned",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ServerView" }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" }
        }
      }
    },
    "/api/cloud/servers": {
      "get": {
        "tags": ["cloud"],
        "operationId": "listCloudServers",
        "summary": "Virtual servers of this account",
        "responses": {
          "200": {
            "description": "Servers",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": { "$ref": "#/components/schemas/ServerView" }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" }
        }
      }
    },
    "/api/cloud/servers/{id}": {
      "delete": {
        "tags": ["cloud"],
        "operationId": "deleteCloudServer",
        "summary": "Delete a virtual server and stop billing",
        "parameters": [
          { "name": "id", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" } }
        ],
        "responses": {
          "200": { "description": "Deleted" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      }
    },
    "/api/cloud/servers/{id}/manage": {
      "get": {
        "tags": ["cloud"],
        "operationId": "manageCloudServer",
        "summary": "One time link to the server console",
        "parameters": [
          { "name": "id", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" } }
        ],
        "responses": {
          "200": {
            "description": "Redirect target",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "redirectUrl": { "type": "string", "format": "uri" }
                  }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      }
    },
    "/api/game/catalog": {
      "get": {
        "tags": ["game"],
        "operationId": "getGameCatalog",
        "summary": "Games with the lowest hourly price for this account",
        "responses": {
          "200": {
            "description": "Games",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": { "$ref": "#/components/schemas/GameCardView" }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" }
        }
      }
    },
    "/api/game/catalog/{slug}": {
      "get": {
        "tags": ["game"],
        "operationId": "getGameDetail",
        "summary": "Versions and plans of one game",
        "parameters": [
          { "name": "slug", "in": "path", "required": true, "schema": { "type": "string" } }
        ],
        "responses": {
          "200": {
            "description": "Game",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/GameDetailView" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      }
    },
    "/api/game/order": {
      "post": {
        "tags": ["game"],
        "operationId": "orderGameServer",
        "summary": "Create a game server",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/GameOrderRequest" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Server being provisioned",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/GameServerView" }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" }
        }
      }
    },
    "/api/game/servers": {
      "get": {
        "tags": ["game"],
        "operationId": "listGameServers",
        "summary": "Game servers of this account",
        "responses": {
          "200": {
            "description": "Servers",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": { "$ref": "#/components/schemas/GameServerView" }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" }
        }
      }
    },
    "/api/game/servers/{id}": {
      "delete": {
        "tags": ["game"],
        "operationId": "deleteGameServer",
        "summary": "Delete a game server and stop billing",
        "parameters": [
          { "name": "id", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" } }
        ],
        "responses": {
          "200": { "description": "Deleted" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "JWT",
        "description": "Access token from /api/auth/login or /api/auth/register, valid for 15 minutes. See https://datapasa.com/auth.md"
      }
    },
    "responses": {
      "Tokens": {
        "description": "Access and refresh tokens",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/AuthTokensResponse" }
          }
        }
      },
      "BadRequest": {
        "description": "Validation error or invalid token",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/ErrorResponse" }
          }
        }
      },
      "Unauthorized": {
        "description": "Missing, expired or invalid credentials",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/ErrorResponse" }
          }
        }
      },
      "Forbidden": {
        "description": "Device check pending (FINGERPRINT_REQUIRED), account restricted or disabled",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/ErrorResponse" },
            "example": { "code": "FINGERPRINT_REQUIRED", "message": "Device check required" }
          }
        }
      },
      "NotFound": {
        "description": "Not found"
      },
      "Conflict": {
        "description": "Email already registered or already verified",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/ErrorResponse" },
            "example": { "code": "EMAIL_TAKEN", "message": "This email is already taken" }
          }
        }
      },
      "RateLimited": {
        "description": "Too many requests",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/ErrorResponse" },
            "example": { "code": "RATE_LIMITED", "message": "Too many requests", "retryAfterSeconds": 600 }
          }
        }
      }
    },
    "schemas": {
      "ErrorResponse": {
        "type": "object",
        "required": ["code", "message"],
        "properties": {
          "code": { "type": "string", "examples": ["INVALID_CREDENTIALS", "FINGERPRINT_REQUIRED", "RATE_LIMITED", "EMAIL_TAKEN"] },
          "message": { "type": "string" },
          "retryAfterSeconds": { "type": "integer" }
        }
      },
      "RegisterRequest": {
        "type": "object",
        "required": ["firstname", "lastname", "email", "password", "acceptedTerms"],
        "properties": {
          "firstname": { "type": "string", "maxLength": 255 },
          "lastname": { "type": "string", "maxLength": 255 },
          "email": { "type": "string", "format": "email", "maxLength": 255 },
          "password": { "type": "string", "minLength": 8, "maxLength": 128 },
          "acceptedTerms": { "type": "boolean", "const": true, "description": "Acceptance of https://datapasa.com/en/terms, https://datapasa.com/en/privacy-policy and https://datapasa.com/en/acceptable-use" },
          "referralCode": { "type": "string", "maxLength": 16 }
        }
      },
      "LoginRequest": {
        "type": "object",
        "required": ["email", "password"],
        "properties": {
          "email": { "type": "string", "format": "email" },
          "password": { "type": "string" }
        }
      },
      "RefreshRequest": {
        "type": "object",
        "required": ["refreshToken"],
        "properties": {
          "refreshToken": { "type": "string" }
        }
      },
      "TokenRequest": {
        "type": "object",
        "required": ["token"],
        "properties": {
          "token": { "type": "string" }
        }
      },
      "EmailRequest": {
        "type": "object",
        "required": ["email"],
        "properties": {
          "email": { "type": "string", "format": "email" }
        }
      },
      "ResetPasswordRequest": {
        "type": "object",
        "required": ["token", "password"],
        "properties": {
          "token": { "type": "string" },
          "password": { "type": "string", "minLength": 8, "maxLength": 128 }
        }
      },
      "AuthTokensResponse": {
        "type": "object",
        "required": ["accessToken", "expiresIn", "refreshToken"],
        "properties": {
          "accessToken": { "type": "string", "description": "JWT bearer token" },
          "expiresIn": { "type": "integer", "description": "Access token lifetime in seconds", "examples": [900] },
          "refreshToken": { "type": "string", "description": "Opaque token valid for 30 days, rotated on every refresh" }
        }
      },
      "MeResponse": {
        "type": "object",
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "email": { "type": ["string", "null"] },
          "firstname": { "type": "string" },
          "lastname": { "type": "string" },
          "roles": { "type": "array", "items": { "type": "string" } },
          "emailVerified": { "type": "boolean" },
          "avatarKey": { "type": ["string", "null"] },
          "fingerprintRequired": { "type": "boolean" },
          "restricted": { "type": "boolean" },
          "restrictionTags": { "type": "array", "items": { "type": "string" } },
          "passkeyCount": { "type": "integer" },
          "verificationRetryAfterSeconds": { "type": "integer" },
          "acceptedLegalVersions": { "type": "object", "additionalProperties": { "type": "string" } },
          "currentLegalVersions": { "type": "object", "additionalProperties": { "type": "string" } }
        }
      },
      "Money": {
        "type": "number",
        "description": "Amount in USD"
      },
      "PublicTariffView": {
        "type": "object",
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "name": { "type": "string", "examples": ["VPS-1"] },
          "groupId": { "type": ["string", "null"], "format": "uuid" },
          "cores": { "type": "integer" },
          "ramMb": { "type": "integer" },
          "diskGb": { "type": "integer" },
          "netMbits": { "type": "integer" },
          "trafficTb": { "type": "integer", "description": "Included traffic per month, 0 when unmetered" },
          "trafficUnmetered": { "type": "boolean" },
          "hourlyPrice": { "$ref": "#/components/schemas/Money" },
          "monthlyPrice": { "$ref": "#/components/schemas/Money", "description": "hourlyPrice multiplied by 730 hours" },
          "popular": { "type": "boolean" },
          "sortOrder": { "type": "integer" }
        }
      },
      "PublicLocationView": {
        "type": "object",
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "country": { "type": "string", "examples": ["Germany"] },
          "countryCode": { "type": "string", "examples": ["DE"] },
          "name": { "type": "string", "examples": ["Cloud-1"] },
          "tariffs": { "type": "array", "items": { "$ref": "#/components/schemas/PublicTariffView" } }
        }
      },
      "TariffGroupView": {
        "type": "object",
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "name": { "type": "string", "examples": ["Standard", "Unmetered", "Cheap"] },
          "sortOrder": { "type": "integer" }
        }
      },
      "PublicTierView": {
        "type": "object",
        "properties": {
          "name": { "type": "string" },
          "thresholdUsd": { "$ref": "#/components/schemas/Money" },
          "tariffNames": { "type": "array", "items": { "type": "string" } }
        }
      },
      "PublicReferralView": {
        "type": "object",
        "properties": {
          "commissionRate": { "type": "number", "examples": [0.15] },
          "promoDiscountRate": { "type": "number", "examples": [0.05] },
          "minPayout": { "$ref": "#/components/schemas/Money" },
          "tiers": { "type": "array", "items": { "$ref": "#/components/schemas/PublicTierView" } }
        }
      },
      "PublicCatalogResponse": {
        "type": "object",
        "properties": {
          "locations": { "type": "array", "items": { "$ref": "#/components/schemas/PublicLocationView" } },
          "groups": { "type": "array", "items": { "$ref": "#/components/schemas/TariffGroupView" } },
          "referral": { "$ref": "#/components/schemas/PublicReferralView" }
        }
      },
      "PublicGamePlanView": {
        "type": "object",
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "name": { "type": "string" },
          "cpuLabel": { "type": "string" },
          "cores": { "type": "integer" },
          "ramGb": { "type": "integer" },
          "diskGb": { "type": "integer" },
          "databases": { "type": "integer" },
          "backups": { "type": "integer" },
          "locationLabel": { "type": "string" },
          "ddosProtection": { "type": "boolean" },
          "dedicatedIp": { "type": "boolean" },
          "hourlyPrice": { "$ref": "#/components/schemas/Money" },
          "monthlyPrice": { "$ref": "#/components/schemas/Money" },
          "popular": { "type": "boolean" },
          "sortOrder": { "type": "integer" }
        }
      },
      "PublicGameView": {
        "type": "object",
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "slug": { "type": "string", "examples": ["minecraft"] },
          "name": { "type": "string" },
          "shortDescription": { "type": ["string", "null"] },
          "cardImageKey": { "type": ["string", "null"], "description": "Relative to https://cdn.datapasa.com/" },
          "bannerImageKey": { "type": ["string", "null"] },
          "versions": { "type": "array", "items": { "type": "string" } },
          "plans": { "type": "array", "items": { "$ref": "#/components/schemas/PublicGamePlanView" } },
          "sortOrder": { "type": "integer" }
        }
      },
      "PublicGamesResponse": {
        "type": "object",
        "properties": {
          "games": { "type": "array", "items": { "$ref": "#/components/schemas/PublicGameView" } }
        }
      },
      "BalanceResponse": {
        "type": "object",
        "properties": {
          "amount": { "$ref": "#/components/schemas/Money" },
          "currency": { "type": "string", "const": "USD" },
          "bonus": {
            "type": "object",
            "properties": {
              "cloud": { "$ref": "#/components/schemas/Money" },
              "game": { "$ref": "#/components/schemas/Money" },
              "total": { "$ref": "#/components/schemas/Money" }
            }
          }
        }
      },
      "PaymentMethod": {
        "type": "object",
        "properties": {
          "code": { "type": "string", "examples": ["stripe", "payram"] },
          "name": { "type": "string" },
          "currency": { "type": "string" }
        }
      },
      "TopUpRequest": {
        "type": "object",
        "required": ["amount", "method"],
        "properties": {
          "amount": { "$ref": "#/components/schemas/Money", "minimum": 5 },
          "method": { "type": "string", "description": "code from /api/payments/methods" },
          "replacePending": { "type": "boolean", "default": false }
        }
      },
      "TopUpResponse": {
        "type": "object",
        "properties": {
          "invoiceId": { "type": "string", "format": "uuid" },
          "paymentUrl": { "type": "string", "format": "uri", "description": "Hosted checkout page for the customer" }
        }
      },
      "InvoiceStatusResponse": {
        "type": "object",
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "status": { "type": "string" },
          "paymentUrl": { "type": ["string", "null"], "format": "uri" }
        }
      },
      "OsImage": {
        "type": "object",
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "name": { "type": "string" },
          "code": { "type": "string" },
          "sortOrder": { "type": "integer" }
        }
      },
      "OrderTariffView": {
        "type": "object",
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "locationId": { "type": "string", "format": "uuid" },
          "name": { "type": "string" },
          "cores": { "type": "integer" },
          "ramMb": { "type": "integer" },
          "diskGb": { "type": "integer" },
          "netMbits": { "type": "integer" },
          "groupId": { "type": ["string", "null"], "format": "uuid" },
          "hourlyPrice": { "$ref": "#/components/schemas/Money" },
          "effectiveHourlyPrice": { "$ref": "#/components/schemas/Money", "description": "Price for this account after discounts and multipliers" },
          "priceMultiplier": { "type": ["number", "null"] },
          "priceMultiplierReason": { "type": ["string", "null"] },
          "trafficTb": { "type": "integer" },
          "trafficUnmetered": { "type": "boolean" },
          "popular": { "type": "boolean" },
          "available": { "type": "boolean" }
        }
      },
      "OrderFormLocation": {
        "type": "object",
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "country": { "type": "string" },
          "countryCode": { "type": "string" },
          "name": { "type": "string" },
          "tariffs": { "type": "array", "items": { "$ref": "#/components/schemas/OrderTariffView" } }
        }
      },
      "OrderFormResponse": {
        "type": "object",
        "properties": {
          "locations": { "type": "array", "items": { "$ref": "#/components/schemas/OrderFormLocation" } },
          "osImages": { "type": "array", "items": { "$ref": "#/components/schemas/OsImage" } },
          "groups": { "type": "array", "items": { "$ref": "#/components/schemas/TariffGroupView" } }
        }
      },
      "OrderRequest": {
        "type": "object",
        "required": ["locationId", "tariffId", "osImageId"],
        "properties": {
          "locationId": { "type": "string", "format": "uuid" },
          "tariffId": { "type": "string", "format": "uuid" },
          "osImageId": { "type": "string", "format": "uuid" }
        }
      },
      "ServerView": {
        "type": "object",
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "name": { "type": "string" },
          "hostname": { "type": "string" },
          "status": { "type": "string" },
          "locationName": { "type": ["string", "null"] },
          "tariffName": { "type": ["string", "null"] },
          "ipv4": { "type": ["string", "null"] },
          "ipv6": { "type": ["string", "null"] },
          "hourlyPrice": { "$ref": "#/components/schemas/Money" },
          "effectiveHourlyPrice": { "$ref": "#/components/schemas/Money" },
          "discountRate": { "type": "number" },
          "priceMultiplier": { "type": ["number", "null"] },
          "priceMultiplierReason": { "type": ["string", "null"] },
          "free": { "type": "boolean" },
          "expiresAt": { "type": "string", "format": "date-time", "description": "End of the hour already paid for" },
          "createdAt": { "type": "string", "format": "date-time" }
        }
      },
      "GameCardView": {
        "type": "object",
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "name": { "type": "string" },
          "slug": { "type": "string" },
          "shortDescription": { "type": ["string", "null"] },
          "cardImageKey": { "type": ["string", "null"] },
          "fromHourlyPrice": { "type": ["number", "null"] }
        }
      },
      "GameVersionView": {
        "type": "object",
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "name": { "type": "string" }
        }
      },
      "GamePlanView": {
        "type": "object",
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "name": { "type": "string" },
          "cpuLabel": { "type": "string" },
          "cores": { "type": "integer" },
          "ramGb": { "type": "integer" },
          "diskGb": { "type": "integer" },
          "databases": { "type": "integer" },
          "backups": { "type": "integer" },
          "locationLabel": { "type": "string" },
          "ddosProtection": { "type": "boolean" },
          "hourlyPrice": { "$ref": "#/components/schemas/Money" },
          "effectiveHourlyPrice": { "$ref": "#/components/schemas/Money" },
          "popular": { "type": "boolean" }
        }
      },
      "GameDetailView": {
        "type": "object",
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "name": { "type": "string" },
          "slug": { "type": "string" },
          "shortDescription": { "type": ["string", "null"] },
          "description": { "type": ["string", "null"] },
          "bannerImageKey": { "type": ["string", "null"] },
          "cardImageKey": { "type": ["string", "null"] },
          "versions": { "type": "array", "items": { "$ref": "#/components/schemas/GameVersionView" } },
          "plans": { "type": "array", "items": { "$ref": "#/components/schemas/GamePlanView" } },
          "games": { "type": "array", "items": { "$ref": "#/components/schemas/GameCardView" } }
        }
      },
      "GameOrderRequest": {
        "type": "object",
        "required": ["gameId", "versionId", "planId"],
        "properties": {
          "gameId": { "type": "string", "format": "uuid" },
          "versionId": { "type": "string", "format": "uuid" },
          "planId": { "type": "string", "format": "uuid" },
          "name": { "type": ["string", "null"] }
        }
      },
      "GameServerView": {
        "type": "object",
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "name": { "type": "string" },
          "status": { "type": "string" },
          "gameName": { "type": ["string", "null"] },
          "gameSlug": { "type": ["string", "null"] },
          "versionName": { "type": ["string", "null"] },
          "planName": { "type": ["string", "null"] },
          "locationLabel": { "type": ["string", "null"] },
          "address": { "type": ["string", "null"], "description": "host:port once provisioned" },
          "hourlyPrice": { "$ref": "#/components/schemas/Money" },
          "effectiveHourlyPrice": { "$ref": "#/components/schemas/Money" },
          "discountRate": { "type": "number" },
          "expiresAt": { "type": "string", "format": "date-time" },
          "createdAt": { "type": "string", "format": "date-time" }
        }
      }
    }
  },
  "security": [
    { "bearerAuth": [] }
  ]
}
